3. Analysis of Data Quality
The dataset used for this analysis originates from the research paper Video Game Telemetry as a Critical Tool in the Study of Complex Skill Learning (Thompson et al, 2013) and can be found here. From August 12th, 2011 to September 19th, 2011, the researchers solicited members of various online SC2 community hubs for their game replay files, from which I found out about this study. Contributors were also asked to complete a survey that asked about their age, skill level, and playing habits. Since each replay file only contains information about what happens during the course of the one game that the file originated from, it is unable to provide players’ personal details such as age or how long they’ve been playing the game. Additionally, a player’s league isn’t included since that information doesn’t directly affect the game once the matchmaking system is done finding an opponent. The league names, in order of increasing skill, are Bronze, Silver, Gold, Platinum, Diamond, Master, and GrandMaster. Data points with a LeagueIndex value of 8 indicate replay files of professional games that were shared with the public. These replays were subsequently submitted to the study by community members, which is why these replays all have missing values for self-reported variables.
It should be noted that “professional” simply means that those replays are of games that were played by professional players in tournaments. There is no “professional” league in the ladder system. Also, the GrandMaster League is limited to the top 200 players. It is necessary for a player to be successful in the GrandMaster League in order to play SC2 professionally, so there is a chance that professional players can be represented in both leagues in this dataset. However, this is unlikely due to their tendency to hide their identities when playing online as much as possible. This is so that their rivals don’t get opportunities to study them. Therefore, it is highly unlikely that a professional player participated in this survey and willingly submitted his own replays of GrandMaster League games played online to this dataset. Due to the small sample size of professional replays in the dataset (55/3395) and the lack of accompanying player information I decided to remove these replays from the datast.
My analysis begins with loading the csv file containing the dataset and converting integer variables that were imported as factors. I then create a new variable called rank that adds the proper name for each player’s league based on the LeagueIndex variable. While the variables derived from the replay files are inherently accurate, the variables derived from survey answers should be checked for inaccuracies and irregularities.
library(tidyverse)
sc <- read.csv('StarCraft2ReplayAnalysis-1.csv')
#Remove professional replays
sc <- filter(sc, sc$LeagueIndex != 8)
#Convert factors back to integers
#Automatically replace "NULL" string values with "NA"
sc$TotalHours <- as.integer(as.character(sc$TotalHours))
sc$HoursPerWeek <- as.integer(as.character(sc$HoursPerWeek))
sc$Age <- as.integer(as.character(sc$Age))
#Add a new variable "League" so that the values in the included "LeagueIndex" variable are associated with the names of each league
rank <- c('Bronze', 'Silver', 'Gold', 'Platinum', 'Diamond', 'Master', 'GrandMaster')
rank <- factor(rank, levels = c('Bronze', 'Silver', 'Gold', 'Platinum', 'Diamond', 'Master', 'GrandMaster'))
sc <- mutate(sc, League = rank[LeagueIndex])
HoursPerWeek and TotalHours
Since my analysis will partly be focused on the change in player metrics as a function of time, it would be appropriate to start with the two time variables, HoursPerWeek and “TotalHours”. These variables are prone to reporting inaccuracy due to the fact that participants are attempting to generalize several months of playing experience and habits into two numbers. By removing outlier values and drawing scatterplots, it is also obvious that the reported values for both variables have been rounded. For TotalHours, the rounding looks to have been done by the respondents since there are data points scattered between rounded values. For HoursPerWeek, I assume that respondents could only choose from certain values preselected by the researchers rather than typing their answer manually.
ggplot(sc, aes(sc$HoursPerWeek)) + geom_density(bw = 2.5, fill = 'purple', alpha = .5) + labs(x = 'Reported Hours Played Per Week', title = 'HoursPerWeek Outliers Fenced') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

ggplot(sc, aes(sc$HoursPerWeek)) + geom_density(bw = 2.5, fill = 'purple', alpha = .5) + labs(x = 'Reported Hours Played Per Week', title = 'HoursPerWeek Outliers Removed') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

summary(sc$HoursPerWeek)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.00 8.00 12.00 15.91 20.00 168.00 1
summary(sc$TotalHours)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
3.0 300.0 500.0 960.4 800.0 1000000.0 2
What immediately stands out in the summary statistics and the density plots are the maximum values for both variables. The maximum value for HoursPerWeek corresponds to 24 hours per day of playing time every week, while the maximum reported value of 1,000,000 for TotalHours far exceeds the number of hours that had elapsed since SC2 was released to the time that the data collection period concluded. When I discussed this matter with Professor Robbins, one of her suggestions was to “fence” the values such that outliers are replaced with a sensible ceiling value. From interviews of professional players as well as hearsay from those who keep in contact with them, I chose 98 hours played per week as a ceiling value. This corresponds to 14 hours played per day, which is a habit that could most likely only be sustained by professional players signed to a major team. Such teams often operate a “team house” where all the players live and train together. It is not uncommon for the sponsors to hire maids to cook and clean so that the players can focus all their attention on playing SC2. In such cases, it is not unusual for players to devote 10-14 hours per day to practicing for upcoming important matches.
Another suggestion was to simply remove these outliers from the dataset, since the respondents might not have given a serious answer to the question. When both methods are compared side by side below, there doesn’t seem to be any appreciable difference due to the small number of outliers (56 out of 3,339). As for the single data point with a value of 0 hours per week, I interpreted that as the respondent playing the game very infrequently. Because there was only one such data point and the fact that the next closest value is 2, I decided not to remove it.
#Restrict "HoursPerWeek" to a maximum value of 98
sc_restrict <- sc
sc_restrict$HoursPerWeek <- ifelse(sc_restrict$HoursPerWeek > 98, 98, sc_restrict$HoursPerWeek)
ggplot(sc_restrict, aes(sc_restrict$HoursPerWeek)) + geom_density(bw = 2.5, fill = 'purple', alpha = .5) + labs(x = 'Reported Hours Played Per Week', title = 'HoursPerWeek Outliers Fenced') + scale_x_continuous(breaks = c(seq(0, 100, by = 5))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

#Remove data with "HoursPerWeek" values greater than 98
sc_remove <- sc
sc_remove <- filter(sc, HoursPerWeek <= 98)
summary(sc_restrict$HoursPerWeek)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
0.00 8.00 12.00 15.87 20.00 98.00 1
summary(sc_remove$HoursPerWeek)
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0 8.0 12.0 15.8 20.0 98.0
ggplot(sc_remove, aes(sc_remove$HoursPerWeek)) + geom_density(bw = 2.5, fill = 'purple', alpha = .5) + labs(x = 'Reported Hours Played Per Week', title = 'HoursPerWeek Outliers Removed') + scale_x_continuous(breaks = c(seq(0, 100, by = 5))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

This variable looks to be unimodal after adjusting the bandwidth of the density plot, with a mode of approximately 7 and a mean of 15.87. The mean value corresponds to around 2 hours per day of play, which sounds reasonable for that time period, during which the game was still relatively new and the fanbase was at its largest. The distribution is right-skewed, which gives an idea of how many ‘typical’ players there are compared to the more dedicated ones
Similarly, I tried both restricting the maximum value for TotalHours to 8,106 and removing data points that exceed that value. I arrived at this theoretical maximum by counting the number of days from the earliest date that a member of the general public could begin playing the game consistently (the pre-release beta test period began on February 17th, 2010) to the end of the data collection period, September 19th, 2011. Assuming that an extremely dedicated fan played the theoretical maximum of 14 hours per day every day during that period, he or she will have played the game for a total of 8,106 hours.
#Restrict "TotalHours" to a maximum value of 8,106
sc_restrict$TotalHours <- ifelse(sc_restrict$TotalHours > 8106, 8106, sc_restrict$TotalHours)
summary(sc_restrict$TotalHours)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
3.0 300.0 500.0 650.2 800.0 8106.0 2
ggplot(sc_restrict, aes(sc_restrict$TotalHours)) + geom_histogram(binwidth = 200, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Restricted') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

#Remove data with "TotalHours" values greater than 8,106
sc_remove <- filter(sc_remove, TotalHours <= 8106)
summary(sc_remove$TotalHours)
Min. 1st Qu. Median Mean 3rd Qu. Max.
3.0 300.0 500.0 633.7 800.0 6000.0
ggplot(sc_remove, aes(sc_remove$TotalHours)) + geom_histogram(binwidth = 200, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Removed') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

Again, both methods for dealing with outliers yield almost identical distributions. In the above histograms, several outlier values that are well below 8,106 hours and yet well above the median of 500 can be seen. Sustaining a 14 hour per day SC2 habit in addition to sleep time and any possible school and work responsibilities would be quite difficult. Even a professional player would have trouble keeping up with that level of practice for an extended period of time without feeling mental exhaustion or experiencing wrist injuries. Therefore, I decided to further restrict this variable to a maximum value of 4,000 hours, which corresponds to approximately 7 hours per day.
#Further restrict "TotalHours" to a maximum value of 4,000
sc_restrict$TotalHours <- ifelse(sc_restrict$TotalHours > 4000, 4000, sc_restrict$TotalHours)
summary(sc_restrict$TotalHours)
Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
3.0 300.0 500.0 639.2 800.0 4000.0 2
ggplot() + geom_histogram(data = sc_restrict, aes(x = sc_restrict$TotalHours), binwidth = 200, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Restricted') + scale_x_continuous(breaks = c(seq(0, 4000, 250))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

ggplot() + geom_density(data = sc_restrict, aes(x = sc_restrict$TotalHours), bw = 75, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Restricted') + scale_x_continuous(breaks = c(seq(0, 4000, 250))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

#Further remove data with "TotalHours" values greater than 4,000
sc_remove <- filter(sc_remove, TotalHours <= 4000)
summary(sc_remove$TotalHours)
Min. 1st Qu. Median Mean 3rd Qu. Max.
3.0 300.0 500.0 625.2 800.0 4000.0
ggplot() + geom_histogram(data = sc_remove, aes(x = sc_remove$TotalHours), binwidth = 200, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Removed') + scale_x_continuous(breaks = c(seq(0, 4000, 250))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

ggplot() + geom_density(data = sc_remove, aes(x = sc_remove$TotalHours), bw = 75, fill = 'purple', alpha = .5) + labs(x = 'Reported Total Number of Hours Played', title = 'TotalHours Outliers Removed') + scale_x_continuous(breaks = c(seq(0, 4000, 250))) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

By removing the outlier values, the tail small bump at 4,000 hours is noticeably reduced. The distribution for TotalHours looks to have a larger variance compared to that of HoursPerWeek. Both distributions are similarly right-skewed, which is to be expected since these two variables are most certainly correlated. Depending on the parameter settings for the density plot and histogram, there might be evidence of bimodality, with a distinct mode of approximately 450 and another possible one of at approximately 700. The mean of 639.2 corresponds to approximately 1.5 hours of SC2 per day, which translates to around four average length 1 vs. 1 matches.
Age
Although age is only an indirect indicator of the passage of time, I want to search for trends across different age groups. In the Starcraft community, it is commonly believed that one’s skill declines with age, such that past the early to mid 20’s, hand movement and reaction speeds begin to slow down. This belief is reinforced by the fact that an overwhelming majority of tournament champions are relatively young, often still in their teens. While the researchers plan to investigate whether or not such a physiological decline actually occurs in their next study, I feel that it would be sensible to see if there’s any supporting evidence for this belief using this dataset, even though it’s not as scientifically valid as analyzing a large quantity of replay files associated with individual players’ experiences with the game over a long period of time.
#Continuing the analysis by removing outlier values rather than restricting them
sc <- sc_remove
summary(sc$Age)
Min. 1st Qu. Median Mean 3rd Qu. Max.
16.00 19.00 21.00 21.66 24.00 44.00
ggplot(sc %>% filter(HoursPerWeek < 50), mapping = aes(x = League, y = Age)) + geom_col(alpha= 0.3, color = 'purple') + labs(title='Age vs. League') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

ggplot(sc, aes(sc$Age)) + geom_histogram(binwidth = 1, fill = 'purple', alpha = .5) + scale_x_continuous(breaks = c(seq(10,44, 2))) + labs(x = 'Age of the Participant') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray'))

ggplot(sc, aes(sc$Age)) + geom_density(bw = .5, fill = 'purple', alpha = .5) + labs(x = 'Age of the Participant') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + scale_x_continuous(breaks = c(seq(10,44, 2)))

A noticeable feature of this distribution is the abrupt cutoff point at 16. This is most likely due to ethical restrictions that the researchers adhered to with respect to allowing minors to participate in the study, rather than a lack of younger SC2 players. Were it not for this, the age distribution would look almost normally distributed, with a mode of 21 and mean of 21.65. The distribution proceeds fairly smoothly to the maximum age of 44. Due to the smoothness of the distribution, I decided to not treat participants in their early 40’s as outliers. The slope of the distribution significantly decreases past age 22, which lends support to the community’s perceived notions concerning age and skill level.
APM (Actions per Minute)
Proceeding to the in-game metrics, I feel that the most obvious one to explore is APM, short for Actions Per Minute. An action is defined to be any key press or mouse click performed by the player. In SC2, APM is almost synonymous with level skill. It is typically understood that if a player can’t perform actions at a rate that is comparable to that of the opponent, then he or she simply can’t keep up and will eventually be overwhelmed due to the fact that the opponent can accomplish more in the same amount of time. The description accompanying this dataset does not specify whether the included APM values are average or maximum values achieved during each game. I assume that they represent average values since average APM is one of the metrics that the game presents to the player at the conclusion of each game.
During battles, a skilled player should be able to position his army properly and micromanage several units simultaneously without neglecting the more mundane tasks of producing workers and constructing buildings back at base. Winning a battle while forgetting to manage one’s bases could leave one in a worse position and allow the opponent to catch up in the near future.
summary(sc$APM)
Min. 1st Qu. Median Mean 3rd Qu. Max.
22.06 79.12 107.00 114.30 139.90 389.80
ggplot(sc, aes(sc$APM)) + geom_density(bw = 3, fill = 'purple', alpha = .5) + labs(x = 'Actions Per Minute') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + scale_x_continuous(breaks = c(seq(20,400, 20)))

The distribution for APM looks to be approximately normally distributed, with a median of 108 and a mean of 117. The slope on the left side is much steeper, with a minimum value of 20. This sounds reasonable, since 20 actions per minute can be achieved by nearly any able-bodied person using a computer without much effort. On the other end, we see some values exceeding 360, which translates to a sustained rate of 6 actions per second. Though there have been a few professional players who were known for consistently attaining such exceptionally high APM values, it could also be the case that the APM outlier values in this dataset are due to some players inflating their APM by rapidly performing meaningless actions, such as clicking empty spaces. Often, players do this at the beginning of a game to warm up their hand muscles since there’s typically not much to do at such an early stage of a game. However, it could be the case that some players continue doing this throughout their games, possibly with the intention of inflating their APM rate. A few years after these replays were collected, an improved APM measurement that filters out such meaningless actions was added to the game. Using that metric, we would be unlikely to see such extreme values.
ggplot(sc, aes(sc$League, sc$APM)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Actions per Minute') + scale_y_continuous(breaks = c(seq(0, 400, 25)))

Plotting the distribution of APM versus league rank supports the idea these outlier APM values were artificially inflated. They originate from players in Diamond and Master League, which is far from the highest levels of competitive play. These outliers are far removed from the median values of their respective leagues. The fact that several values exceed even the highest APM for GrandMaster players suggests that they are not realistically attainable values for players in these leagues. Therefore, I decided to remove data points where APM values exceed 275, which is the approximate median value for the professional players represented in this dataset.
#Remove data points belonging to Diamond and Master League that have over 275 APM
sc <- sc[!(sc$APM > 275 & sc$League == 'Diamond'),]
sc <- sc[!(sc$APM > 275 & sc$League == 'Master'),]
ggplot(sc, aes(sc$APM)) + geom_density(bw = 3, fill = 'purple', alpha = .5) + labs(x = 'Actions Per Minute') + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + scale_x_continuous(breaks = c(seq(20,400, 20)))

ggplot(sc, aes(sc$League, sc$APM)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.5, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Actions Per Minute') + scale_y_continuous(breaks = c(seq(0, 400, 25)))

Having removed these outlier values, the shape of the density plot did not change much, but the boxplot looks more reasonable. There is a positive correlation between League and APM that supports the idea that APM is a reasonable indicator of skill level. However, it should be noted that player actions can rarely be accurately categorized as either “meaningful” or “meaningless”. There are often several events happening simultaneously across the battlefield that demand a player’s attention. SC2 expertise heavily depends on the ability to prioritize these events and focus attention on the most important ones. To give two extreme examples, a player who devote full attention to only controlling a single unit at a time will most likely not win too many games, much like a player who instantly shifts his or her attention to the latest event without any prioritization. Even with “meaningful” actions, the decisions that a player makes can be judged on a continuous scale of effectiveness that depends on the context of what is happening and what the player knows at that moment. In broadcasted games, commentators are expected to analyze these decisions and point out which ones they think are wise or unwise. Thus, one cannot declare that one player is definitely better than another player based solely on a comparison of APM.
5. Main Analysis
It is commonly thought that higher APM comes naturally through becoming more skilled at SC2. In the SC2 community, it is believed that there is a rough heirarchy of skills that one should acquire in order to become skilled. The variables corresponding to these skills, ranked in approximate descending order of importance, are the following:
- WorkersMade
- MinimapAttacks / MinimapRightClicks
- AssignToHotkeys / SelectByHotkeys
- TotalMapExplored
- UniqueUnitsMade
- ComplexAbilitiesUsed / ComplexUnitsMade
- UniqueHotkeys
It should be noted that these variables are rates expressed in “timestamps”, which is the way that the game calculates time. There are approximately 88.5 timestamps per second according to the dataset documentation. Since such a short measure of time creates very small values, I will express these variables in minutes to make the data and graphs easier to understand. Throughout this section, I encountered outliers for several variables that made it difficult to read the graphs. I dealt with these outlier values by removing them from the dataset. I made this decision due to these data points comprising an extremely small portion of the dataset. Also, since most of these metrics are more obscure and not discussed often in the community, it would be difficult to decide on a “reasonable” ceiling value to restrict these variables to.
In addition, I decided to omit the MaxTimeStamp variable from my analysis. This variable indicates the length of each game. While there may be some correlation between how skilled a player is and his or her average game length, each player’s choice in strategy has a major effect on how long a particular game will be. For example, before a game begins, a player can decide to invest significant resoures early on into doing a quick surprise attack to catch the opponent off guard. If such an attack fails, then the attacking player will be in a vulnerable position and may be defeated by the opponent’s counterattack. In this case, the game is likely to end early regardless of the outcome. On the other hand, both players can just as easily decide to focus on their own army and building production while avoiding conflicts early on. Such a game would take much longer to finish. Since game length is largely affected by conscious choices that players make in addition to behavior derived from skill level, it would be difficult to isolate these two factors in orde to assess MaxTimeStamp as an indicator of skill level.
1) WorkersMade
The typical answer to the question of how one gets better at SC2 is to make more workers. This might seem surprisingly at first. Worker units are primarily responsible for collecting resources and constructing buildings. This might sound quite mundate, but collecting enough resources is of utmost importance, since this is the currency with which players build their army and bases. Collecting resources at a suboptimal rate due to having too few workers will create a bottleneck that will slow the rate at which your army can be produced. Additionally, workers can be serve as scouts or sacrificial pawns during attacks and defenses. People often tell novices that they can get promoted out of Bronze League with this simple strategy:constantly produce workers, collect lots of resources, build an army with those resources (the type of units built doesn’t really matter), and send that army straight to the enemy base.
The violin plot below shows that there is a slightly positive correlation between how workers are produced per minute and the rank of the player. By looking at the median lines, one can see a slight downward trend going from Master to GrandMaster. A more pronounced increase can be seen when looking at the outlier values from Diamond to GrandMaster. This demonstrates that one should find the proper balance between making too few workers (collecting resources at a suboptimal rate) and making too many (wasting money by producing unneeded workers who have nothing to do). On the other end, the transition from Bronze to Silver results in the median increasing from 2.934 to 3.84, which is more than the transition between any other pairs of adjacent leagues. This supports the claim that players can be promoted out of Bronze League simply by focusing on improving their worker production habits. Meanwhile, worker production rates level out at the higher leagues. The outlier values in Diamond league could be a sign of players producing more workers than needed, which would be an inefficient use of resources.
ggplot(sc, aes(sc$League, sc$WorkersMade*88.5*60)) + geom_violin(fill = 'purple', alpha = 0.5, draw_quantiles = c(0.5)) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.5, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Workers Produced per Minute') + scale_y_continuous(breaks = c(seq(0,30, 2)))

#Display summary statistics for "WorkersMade" for each league
for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$WorkersMade*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.4089 2.1570 2.9340 3.3260 4.0100 10.9400
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.8776 2.8510 3.8400 4.2540 5.0620 14.7200
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.508 3.323 4.292 4.871 6.006 19.840
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.284 3.616 4.689 5.344 6.406 18.850
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.482 4.207 5.391 6.188 7.469 27.340
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.496 4.372 5.628 6.427 7.673 21.880
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
2.570 3.777 5.333 6.413 7.267 16.940
2) MinimapAttacks / MinimapRightClicks
The second most important metric might be a surprising choice as well. The minimap is a map of the battlefield located on the bottom left corner of the user interface (as shown in the first gameplay screenshot in the introduction section). At a glance, players can see what is happening wherever he has an army presence. By clicking on the minimap, players can instantly switch views to the corresponding location. More importantly, he or she can order units to move to or attack that location. The alternative is for the player to manually scroll to that location with the mouse,which could take much longer depending on how big the battlefield is and where the playter was looking prior to scrolling. With the minimap, players can observe and react to situations occurring in the game faster and more efficiently. This difference can be compared to the difference between editing a long document with ability to jump to different sections by clicking or scrolling with a mouse versus having to manually scroll through the text using the arrow keys on the keyboard.
ggplot(sc, aes(sc$League, sc$MinimapAttacks*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Minimap Attacks Issued per Minute')

ggplot(sc, aes(sc$League, sc$MinimapRightClicks*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Minimap Right Clicks Performed per Minute')

There are a few outlier data points that are obscuring the view of the league distributions for MinimapAttacks (17/3395) and MinimapRightClicks (7/3395). After removing these points, the distributions can be more clearly seen:
#Remove the "MinimapAttacks" outlier values and convert the values in terms of minutes
sc <- filter(sc, sc$MinimapAttacks <= 0.001003)
ggplot(sc, aes(sc$League, sc$MinimapAttacks*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.5, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Minimap Attacks Issued per Minute') + scale_y_continuous(breaks = c(seq(0,7, 0.25)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$MinimapAttacks*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.1502 0.1519 1.3840
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.00000 0.07434 0.23670 0.24000 4.66900
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.1131 0.2838 0.3807 2.9940
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.1845 0.3974 0.5104 4.7190
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.07301 0.30320 0.55590 0.69710 4.78700
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.1670 0.4848 0.7678 1.0520 5.3250
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.05161 0.71670 1.27500 1.55500 1.95200 4.73800
#Remove the "MinimapRightClicks" outlier values and convert the values in terms of minutes
sc <- filter(sc, sc$MinimapRightClicks <= 0.0028)
ggplot(sc, aes(sc$League, sc$MinimapRightClicks*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.5, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Minimap Right Clicks Performed per Minute') + scale_y_continuous(breaks = c(seq(0, 15, 1)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$MinimapRightClicks*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.3454 0.8103 1.1050 1.6140 7.4630
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.4721 1.0410 1.4630 2.0310 10.7900
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 0.666 1.271 1.745 2.342 12.070
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.7308 1.4410 1.9160 2.5050 10.3900
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.8876 1.6690 2.2530 3.1530 11.4100
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 1.005 1.810 2.459 3.384 13.270
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.1333 1.6350 2.7240 2.9810 3.8270 8.6670
In both cases, there’s a positive correlation that appears to increase with the leagues. For MinimapAttacks, the slope of the LOESS line increases at the upper leagues. The large increase in the median between Master and GrandMaster (0.4867 to 1.277) shows the importance of using the minimap in attaining the highest level of skill. On the other end, the median also changes significantly from Bronze to Silver (o to 0.07434), which supports the belief that at least some minimap usage can be enough to be promoted out of Bronze League. It seems that many players from Bronze all the way to Platinum League don’t issue attack commands through the minimap or do so very rarely. For MinimapRightClicks, the variation between leagues isn’t as pronounced, with a roughly linear LOESS line. In the lower leagues, the median value of 0.8103 for Bronze League indicates that this is an action that most novice players already know to do. Therefore they just need to learn to do so more often.
3) AssignToHotkeys and SelectByHotkeys
It could be argued that learning to use hotkeys is equally essential to improving at SC2. Hotkeys are the computer game equivalent of keyboard shortcuts, and by not using them, one would be reduced to playing the game with only a mouse. To give an idea of how inefficient and time-consuming that would be, imagine having to type by using a mouse to click each letter on a virutal keyboard at the bottom of the computer screen. This analogy is not far from the truth since most of the buttons corresponding to actions in the game are located at the bottom of the user interface.
Each possible command in the game has an associated default hotkey. In addition, players can assign specific groups of units and buildings to one of the number keys. Therefore, by pressing a number key, a player can immediately select several units or buildings simultaneously, no matter where they are on the map or how far apart they are, and issue commands to them. The variable AssignToHotkeys indicates how often a player makes these assignments, whereas SelectByHotkeys indicates how often a player controls these previously assigned groups using the hotkeys.
ggplot(sc, aes(sc$League, sc$AssignToHotkeys*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Hotkey Assignments Made per Minute')

#Remove outlier values for the "AssignToHotkeys" variable
sc <- filter(sc, sc$AssignToHotkeys*88.5*60 <= 6.5)
ggplot(sc, aes(sc$League, sc$AssignToHotkeys*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.5, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Hotkey Assignments Made per Minute') + scale_y_continuous(breaks = c(seq(0,7, 0.5)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$AssignToHotkeys*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.4811 0.8097 0.9849 1.3340 3.5450
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.5614 0.9851 1.1790 1.7030 3.6620
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.8148 1.3810 1.4990 2.0780 4.8760
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.1237 1.0910 1.7520 1.7860 2.3820 5.3920
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.1651 1.5140 2.1630 2.1910 2.7800 6.2370
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.3452 1.9320 2.6080 2.6890 3.3150 6.3600
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
1.645 2.493 3.529 3.592 4.553 6.479
ggplot(sc, aes(sc$League, sc$SelectByHotkeys*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Hotkey Groups Selected per Minute')

After removing those outliers, there are still some data points with outlier values for the SelectByHotkeys variable (32/3395) that should be removed as well.
#Remove outlier values for the "SelectByHotkeys" variable and convert the values in terms of minutes
sc <- filter(sc, sc$SelectByHotkeys*88.5*60 < 150)
ggplot(sc, aes(sc$League, sc$SelectByHotkeys*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Hotkey Groups Selected per Minute') + scale_y_continuous(breaks = c(seq(0,200, 10)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$SelectByHotkeys*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 1.558 3.566 5.741 7.274 74.270
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 3.083 5.813 8.154 9.809 74.290
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 4.692 8.201 11.600 13.500 104.000
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.000 6.661 11.490 16.500 19.900 124.400
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.2825 11.0700 18.2200 24.9900 31.9400 147.0000
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.6472 15.9500 28.2200 36.1500 48.3100 148.7000
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
14.30 30.07 43.55 50.58 65.27 142.20
In Bronze and Silver League, the median and mean values for AssignToHotkeys are close to 1. From my experience with the game, this reflects the habit of assigning one’s entire army to one hotkey and updating the assignment by adding newly produced units to that hotkey group. Players who control their entire army as a single large mass mostly do so because they lack the multitasking skills to effectively control multiple groups at a time. However, this setup makes it difficult for them to split their units up to deal with threats occurring simultaneously in different places. The median and mean values for SelectByHotkeys in the lower leagues are similarly low due to the fact that there’s no need to make too many hotkey selections when your entire army is assigned to only one or two hotkey groups. This type of habit seems to disappear as one progresses through the leagues. Comparing the lower leagues to the higher ones indicates that these variables are more important in being promoted from the middle leagues. The differences between the Diamond, Master, and GrandMaster League distributions are quite pronounced. This illustrates the importance of multitasking in SC2, and specifically, the importance of maintaining many hotkey groups and continuously selecting them to issue orders to attack and defend.
4) TotalMapExplored
The need for intelligence gathering during battles should be apparent. As a consequence of games happening under the fog of war, players have an incentive to constantly scout and observe the opposing player’s actions while hiding their own actions from enemy scouts. As previously mentioned, players can decide to perform sneak attacks early in a game to win the game quickly and decisively. Even in slower paced games, one should be aware of what kind of army the opponent is producing and where that army is moving. In SC2, each army unit has a “rock, paper, scissors” relationship with other types types of units, meaning that each unit is effective at fighting certain types of units while being ineffective against other types of units. Thus, by keeping watch of the opponent’s army, one can respond by producing the types of units that are effective against the opposing army.
In SC2, the battlefield is divided into squares that are about the size of the mouse pointer. The TotalMapExplored metric measures how many 24x24 grids of these squares a player has explored per timestamp. During the course of a game, players will naturally explore territory by virtue of expanding to new bases to collect more resources and sending their army to attack the opponent. However, a skilled player will go beyond this and intentionally send units to different parts of the map to confirm what the opponent is doing and to make sure that no sneak attacks are incoming. Timely information received from scouting can mean the difference between victory and defeat in many games.
ggplot(sc, aes(sc$League, sc$TotalMapExplored*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Number of 24x24 Map Grids Explored per Minute')+ scale_y_continuous(breaks = c(seq(0,5, 0.25)))

The league distribution medians seem to be increasing almost linearly, but I’d like to remove the few outlier values (2/3395) greater than 4.00 to make sure there isn’t a polynomial increase being obscured.
#Remove outlier values for the "TotalMapExplored" variable and convert the values in terms of minutes
sc <- filter(sc, sc$TotalMapExplored < 0.00074)
ggplot(sc, aes(sc$League, sc$TotalMapExplored*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Number of 24x24 Map Grids Explored per Minute') + scale_y_continuous(breaks = c(seq(0,5, 0.25)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$TotalMapExplored*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5726 0.9990 1.2220 1.3300 1.5570 3.6790
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5853 1.0320 1.3040 1.3620 1.5640 3.6100
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.4848 1.0880 1.3060 1.3750 1.5780 3.1050
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.5469 1.1930 1.4040 1.4680 1.6680 3.5180
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.651 1.270 1.515 1.570 1.799 3.903
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.621 1.303 1.548 1.636 1.866 3.696
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.9095 1.2530 1.6510 1.6190 1.8620 2.7500
There’s as relatively large increase in the median going from Bronze to Silver League which indicates that Silver League players leave their bases more often, though it’s unclear whether this is the result of purposeful scouting or just a consequence of being more active on the battlefield when performing other actions. Past Bronze League, it looks like the distributions are relatively until the middle, according to the LOESS line. Knowing to scout seems to be a a skill that’s acquired when transitioning from the Gold to Platinum Leagues. The transition from Master to GrandMaster League shows another relatively large median increase, which indicates that scouting is a crucial skill to acquire in order to reach the highest skill level.
5) UniqueUnitsMade
At first, I thought this variable measured the rate that players were producing units. This would be a measure of a player’s macromanagement and being able to efficiently spend his or her resources. However, upon converting the values to reflect minutes rather than timestamps, I saw that the values were much too low, considering that games typically last for 15-25 minutes and armies often consist of dozens of units.
ggplot(sc, aes(sc$League, sc$UniqueUnitsMade*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Unique Units Made per Minute') + scale_y_continuous(breaks = c(seq(0, 1.1, 0.05)))

These values would imply that on average, only a handful of units are produced per game, which is clearly not correct. I then tried multiplying this variable by MaxTimeStamp, which indicates how many timestamps each game contained, which would indicate the duration of each game. This should provide the total number of unique units produced per game.
ggplot(sc, aes(sc$League, sc$UniqueUnitsMade*sc$MaxTimeStamp)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Unique Units Made per Game') + scale_y_continuous(breaks = c(seq(0,15, 1)))

Notice that all of the values fall between 2 and 13. Given this range, it is most likely that this variable is the number of different types of units produced per game divided by the total length of the game considering that each player can only choose from at most 15 types of units to produce. A value of 2 would indicate that the player produced nothing but workers and the most basic, low-tech attacking unit. Given my new understanding of this variable, I decided that I would not use it any further during this project. While making a diverse army comprised of various types of units can be seen as a measure of how well a player can adapt to changing situations in the game by adjusting the composition of his or her army, it is also true that UniqueUnitsMade is naturally correlated with game length. This is due to the fact that the more advanced, hi-tech units cannot be produced early in the game due to their high costs and prerequisite infrastructure requirements (this idea is explained in more detail in the next section). As mentioned before, game length is in part determined by conscious decisions made by each player, so it would not be appropriate to interpret this metric as pure measure of skill.
6) ComplexAbilitiesUsed / ComplexUnitsMade
In SC2, there is a heirarchy in which all the different types of buildings and army units belong, called the “tech tree”. In the tech tree, buildings and units further up the tree can only be accessed once certain buildings further down in the tech tree have been constructed. This network of dependencies ensures that the inexpensive, low-tech buildings and units are accessible relatively early in the game, whereas the most powerful and expensive units are reserved for later in the game. Often, these units have devastating abilities that can be used repeatedly (with a predetermined recharge period in between uses) to devastate an opposing army if used correctly. This depends on the player manually clicking on the intended target in an accurate and timely manner. Otherwise, the attack misses its target and is wasted.
ComplexAbilitiesUsed measures how often a player uses such targeted abilities, whereas ComplexUnitsMade measures the rate at which these advanced units are produced. Properly utilizing these units and abilities requires another layer of micromanagement on top of everything else that a player needs to attend to during a game. Therefore, one would think that higher skilled players will use them with greater frequency.
ggplot(sc, aes(sc$League, sc$ComplexAbilityUsed*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Targeted Abilities Used per Minute')

ggplot(sc, aes(sc$League, sc$ComplexUnitsMade*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Advanced Units Produced per Minute')

Again, the lower league distributions seem like they might be obscured due to outliers (99/3395 for ComplexAbilityUsed and 3/3395 for ComplexUnitsMade).
#Remove outlier values for "ComplexAbilityUsed" and convert the values in terms of minutes
sc <- filter(sc, sc$ComplexAbilityUsed < 0.00188)
ggplot(sc, aes(sc$League, sc$ComplexAbilityUsed*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Targeted Abilities Used per Minute') + scale_y_continuous(breaks = c(seq(0,10, 0.5)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$ComplexAbilityUsed*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.2217 0.2392 3.3450
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.4015 0.3045 9.3640
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.5606 0.6701 8.3970
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.1625 0.7236 0.9349 9.7880
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.3441 0.9119 1.2080 9.5040
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.2591 0.9162 1.3490 9.1170
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.5945 0.9948 4.3870
#Remove outlier values for "ComplexUnitsMade" and convert the values in terms of minutes
sc <- filter(sc, sc$ComplexUnitsMade < 0.00075)
ggplot(sc, aes(sc$League, sc$ComplexUnitsMade*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Advanced Units Produced per Minute') + scale_y_continuous(breaks = c(seq(0,4, 0.25)))

for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$ComplexUnitsMade*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.00000 0.00000 0.00000 0.07784 0.00000 1.68800
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.1293 0.0000 2.6230
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.2338 0.1362 3.1330
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.3398 0.5252 3.4910
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.3976 0.6984 3.5930
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.4002 0.6635 3.2030
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.0000 0.0000 0.3373 0.4641 2.0490
It appears that the lower league distributions for these two variables truly do look that, meaning that players in Bronze or Silver League use these advanced units and their abilities extremely rarely. In fact, the median values of ComplexUnitsMade for each distribution is 0. When looking at the means for each league, there’s a positive correlation from Bronze to Master League followed by a slight decrease in GrandMaster League. Therefore, these seem to be additional examples of skills that are not particularly used in the lower leagues but are used much more often among more skilled players.
7) UniqueHotkeys
Originally, I had ranked this metric in second place on the skill heirarchy. From the variable name and description from the researchers, I thought that this variable measured how often players used different hotkeys to input commands. However, when I converted the variables in terms of minutes, the values were much too low to make sense when placed in the context of APM and the other hotkey variables. For example, how could a GrandMaster player be using less than one unique hotkey per minute when he is most likely performing over 150 actions per minute? Upon consulting with my friend, we came to the conclusion that this metric actually measures the rate at which hotkeys that the player changed from the default settings. For example, players can change certain hotkey settings so that commonly used commands are assigned to neighboring keys on the keyboard to facilitate the issuing of commands. Conversely, a player can also decide to reassign a little used command as to avoid accidentally pressing it during hectic periods in the game. Given that there’s a steep learning curve involved in transitioning from not using hotkeys at all, beginning to use them, committing them to memory so that one can use them quickly and comfortably, to finally understanding one’s needs and customizing his or her hotkey setup to match that need, I would expect that there wouldn’t be much of a difference in the lower league distributions for this metric, but there would be a pronounced difference in the upper league distributions.
ggplot(sc, aes(sc$League, sc$UniqueHotkeys*88.5*60)) + geom_boxplot(outlier.color = 'blue', fill = 'purple', alpha = 0.5) + geom_smooth(method = 'loess', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'League', y = 'Unique Hotkeys Used per Minute') + scale_y_continuous(breaks = c(seq(0, 2, 0.1)))

#Display summary statistics for "UniqueHotkeys" for each league
for(league in rank) {
print(league)
print(summary(filter(sc, sc$League == league)$UniqueHotkeys*88.5*60))
}
[1] "Bronze"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.1065 0.2267 0.2404 0.3398 1.2800
[1] "Silver"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.1099 0.2124 0.2386 0.3348 0.7454
[1] "Gold"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.1306 0.2283 0.2629 0.3637 1.7920
[1] "Platinum"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.1561 0.2551 0.2775 0.3729 1.2480
[1] "Diamond"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.2023 0.2992 0.3368 0.4402 1.5580
[1] "Master"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.0000 0.2627 0.3725 0.3939 0.4910 1.4970
[1] "GrandMaster"
Min. 1st Qu. Median Mean 3rd Qu. Max.
0.2113 0.2942 0.4269 0.4350 0.5334 0.9168
The data seems to support my intuition. In the Lower Leagues, the LOESS line increases quite slowly, but suddenly increases in slow when transitioning from Platinum to Diamond League. It seems that this variable is not so important for less skilled player, but it’s a deciding factor for how skilled players are in the upper leagues.
Analysis of Each Metric with Respect to Time Variables
Having analyzed each of the relevant variables individually, I’d like to have a quick overview of the correlations between the various in-game metrics and each time variable.
library(corrplot)
library(viridis)
#Visualize correlation coefficients between "Age" and all other variables
compare_age <- data.frame(c(sc[3], sc[6:11], sc[16:17], sc[19:20]))
corr_age <- cor(compare_age)
corrplot.mixed(corr_age, lower = 'number', upper = 'ellipse', col = viridis(256), title = 'Correlations with Player Age', mar=c(0,0,1,0))

#Visualize correlation coefficients between "HoursPerWeek" and all other variables
compare_week <- data.frame(c(sc[4], sc[6:11], sc[16:17], sc[19:20]))
corr_week <- cor(compare_week)
corrplot.mixed(corr_week, lower = 'number', upper = 'ellipse', col = viridis(256), title = 'Correlations with Hours Played per Week', mar=c(0,0,1,0))

#Visualize correlation coefficients between "TotalHours" and all other variables
compare_total <- data.frame(c(sc[5], sc[6:11], sc[16:17], sc[19:20]))
corr_total <- cor(compare_total)
corrplot.mixed(corr_total, lower = 'number', upper = 'ellipse', col = viridis(256), title = 'Correlations with Total Hours Played', mar=c(0,0,1,0))

At first glance, it seems like there are some correlations between some metrics and the three time variables. More importantly, it looks like all but one of the metrics are negatively correlated with age and positively correlated with playing more hours per week and in total. Interestingly enough, for all three time variables, the three variables with the greatest magnitude correlation coefficients are APM, SelectByHotkeys, and AssignToHotkeys.
ggplot(sc, aes(Age, APM)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Player Age', y = 'Actions Per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0, 400, 25)))

ggplot(sc, aes(Age, SelectByHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Player Age', y = 'Hotkey Groups Selected per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,200, 25)))

ggplot(sc, aes(Age, AssignToHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Player Age', y = 'Hotkey Assignments Made per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,7, 0.5)))

It should be noted that there is an obvious correlation between the two hotkey variables and APM, which is due to the fact that proper hotkey usage contributes greatly to having a high APM by virtue of allowing faster and more efficient actions, which save the player time that can be spent on performing more actions. Similarly, This is the case for all of the other variables show in the correlation matrix as well. Therefore, it is natural that these variables would be correlated in the same direction.
One can see that the variance for any given age group decreases with age due to the fact that sample size for older players is much smaller than those in their teens and early twenties. Therefore, it looks like there is some evidence that age negatively affects these metrics.
The only variable with a positive correlation with Age is UniqueHotKeys.
ggplot(sc, aes(Age, UniqueHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Player Age', y = 'Unique Hotkeys Used per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0, 2, 0.1)))

The slope of the regression line looks to be almost 0 unless one is looking closely. Therefore, it seems that age does not have much of an effect on how players customize their hotkey setup. This could be in part due to our previous finding that UniqueHotkeys seems to be the least important metric in the skill heirarchy.
ggplot(sc, aes(HoursPerWeek, APM)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Hours Played Per Week', y = 'Actions Per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0, 400, 25))) + scale_x_continuous(breaks = c(seq(0, 100, by = 5)))

ggplot(sc, aes(HoursPerWeek, SelectByHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Hours Played Per Week', y = 'Hotkey Groups Selected per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,200, 25))) + scale_x_continuous(breaks = c(seq(0, 100, by = 5)))

ggplot(sc, aes(HoursPerWeek, AssignToHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Hours Played Per Week', y = 'Hotkey Assignments Made per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,7, 0.5))) + scale_x_continuous(breaks = c(seq(0, 100, 5)))

These three variables exhibit a positive correlation with HoursPerWeek, with correlation coefficients that have slightly higher magnitudes when compared to those of Age.
ggplot(sc, aes(TotalHours, APM)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Total Number of Hours Played', y = 'Actions Per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0, 400, 25))) + scale_x_continuous(breaks = c(seq(0, 4000, 250)))

ggplot(sc, aes(TotalHours, SelectByHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Total Number of Hours Played', y = 'Hotkey Groups Selected per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,200, 25))) + scale_x_continuous(breaks = c(seq(0, 4000, 250)))

ggplot(sc, aes(TotalHours, AssignToHotkeys*88.5*60)) + geom_point(color = 'purple', alpha = 0.25) + theme(panel.background = element_rect(fill = 'white'), panel.grid.major = element_line(color = 'gray')) + labs(x = 'Reported Total Number of Hours Played', y = 'Hotkey Assignments Made per Minute') + geom_smooth(method = 'lm', se = FALSE, color = 'blue', size = 1.2, aes(group = 1)) + scale_y_continuous(breaks = c(seq(0,7, 0.5))) + scale_x_continuous(breaks = c(seq(0, 4000, 250)))

This set of scatterplots show an even greater positive correlation between the three variables and TotalHours. Although these findings might not be completely accurate due to the values being self-reported, I feel that the correlation is strong enough to warrant future reearch into the relationships among these variables.
LS0tDQp0aXRsZTogRXhhbWluaW5nIEluLUdhbWUgTWV0cmljIEltcG9ydGFuY2UgYW5kIFZhcmlhdGlvbnMgaW4gUGxheWVyIFNraWxsIGluIFN0YXJjcmFmdA0KICBJSQ0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50Og0KICAgIGZpZ19oZWlnaHQ6IDgNCiAgICBmaWdfd2lkdGg6IDEwDQogIGh0bWxfbm90ZWJvb2s6DQogICAgZmlnX2hlaWdodDogOA0KICAgIGZpZ193aWR0aDogMTANCi0tLQ0KDQoNCiMjMS4gSW50cm9kdWN0aW9uDQoNCg0KPiBUaGlzIHByb2plY3QgaXMgYW4gZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcyBvZiBhIGRhdGFzZXQgb3JpZ2luYXRpbmcgZnJvbSB0aGUgY29tcHV0ZXIgZ2FtZSAiU3RhcmNyYWZ0IElJIiAoaGVuY2Vmb3J0aCByZWZlcnJlZCB0byBhcyAiU0MyIiBmb3Igc2hvcnQpLiBTQzIgYmVsb25ncyB0byB0aGUgcmVhbC10aW1lIHN0cmF0ZWd5IChSVFMpIGdlbnJlLCB3aGljaCB0eXBpY2FsbHkgaW52b2x2ZSBwbGF5ZXJzIG1hbmFnaW5nIG1pbGl0YXJ5IGJhc2VzIGFuZCBkaXJlY3RseSBjb250cm9sbGluZyBhcm1pZXMgaW4gb3JkZXIgdG8gY29tYmF0IG9wcG9zaW5nIHBsYXllcnMnIGFybWllcyBhbmQgZGVzdHJveSB0aGVpciBiYXNlcy4gSW4gU0MyLCBwbGF5ZXJzIGNvbnRyb2wgb25lIG9mIHRocmVlIGZhY3Rpb25zLCBlYWNoIHdpdGggY29tcGxldGVseSBkaWZmZXJlbnQgcGxheXN0eWxlcyBhbmQgYWJpbGl0aWVzLiBBbGwgcGxheWVyIGFjdGlvbnMgaGFwcGVuIGluIHJlYWwtdGltZS4gVGh1cywgYSBzdWNjZXNzZnVsIHBsYXllciBtdXN0IGJlIGFibGUgdG8gY29uc3RydWN0IGJ1aWxkaW5ncywgZ2F0aGVyIHJlc291cmNlcywgYW5kIHJhaXNlIGFuIGFybXkgd2hpbGUgc2ltdWx0YW5lb3VzbHkgY29udHJvbGxpbmcgZXhpc3RpbmcgY29tYmF0IHVuaXRzLiBUaGlzIGRpY2hvdG9teSBpcyByZWZlcnJlZCB0byBtYWNyb21hbmFnZW1lbnQgYW5kIG1pY3JvbWFuYWdlbWVudCwgb3IgIm1hY3JvIiBhbmQgIm1pY3JvIiBmb3Igc2hvcnQuIEluIGFkZGl0aW9uLCBlYWNoIHBsYXllcidzIHZpZXcgb2YgdGhlIGJhdHRsZWZpZWxkIGlzIGxpbWl0ZWQgYnkgdGhlICJmb2cgb2Ygd2FyIiwgYSB0ZXJtIHRoYXQgbWVhbnMgdGhhdCBlYWNoIHBsYXllciBjYW4gb25seSBzZWUgaW4gbG9jYXRpb25zIHdoZXJlIGhpcyB1bml0cyBhbmQgYnVpbGRpbmdzIGFyZSBwcmVzZW50LiBPdGhlcndpc2UsIHRoYXQgcGFydCBvZiB0aGUgbWFwIGlzIG9ic2N1cmVkLiBUaHVzLCBhIHN1Y2Nlc3NmdWwgcGxheWVyIG11c3QgYmUgYWJsZSB0byBjb25zdHJ1Y3QgYnVpbGRpbmdzLCBnYXRoZXIgcmVzb3VyY2VzLCBhbmQgcmFpc2UgYW4gYXJteSB3aGlsZSBzaW11bHRhbmVvdXNseSBjb250cm9sbGluZyBleGlzdGluZyBjb21iYXQgdW5pdHMgYW5kIG1haW50YWluaW5nIGF3YXJlbmVzcyBvZiB3aGF0IHRoZSBlbmVteSBpcyBkb2luZyBvbiB0aGUgYmF0dGxlZmllbGQuIEEgbW9yZSBkZXRhaWxlZCBkZXNjcmlwdGlvbiBvZiB0aGUgZ2FtZSB3cml0dGVuIGJ5IHRoZSByZXNlYXJjaGVycyB3aG8gY29tcGlsZWQgdGhpcyBkYXRhc2V0IGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cDovL3NraWxsY3JhZnQuY2EvYWJvdXQtdGhlLXByb2plY3QuaHRtbCkuDQoNCg0KPiBUaGUgcHVycG9zZSBvZiB0aGlzIHN0dWR5IGlzIHRvIGludmVzdGlnYXRlIFNDMiBhcyBhIHRvb2wgZm9yIG1lYXN1cmluZyBob3cgcGVvcGxlIGxlYXJuIGFuZCBhY3F1aXJlIGV4cGVydGlzZS4gVGhlc2UgcmVwbGF5IGZpbGVzIGFyZSBhIGNvbXBsZXRlIHJlY29yZCBvZiBldmVyeXRoaW5nIHRoYXQgaGFwcGVucyBkdXJpbmcgdGhlIGNvdXJzZSBvZiBhIGdhbWUsIGluY2x1ZGluZyB2YXJpb3VzIG1ldHJpY3MgcmVsYXRlZCB0byB0aGUgZ2FtZSBzdGF0ZSwgd2hhdCBlYWNoIHBsYXllciBpcyBsb29raW5nIGF0LCBhbmQgd2hlcmUgZXZlcnkgZ2FtZSB1bml0IGlzIGxvY2F0ZWQgYXQgYW55IGdpdmVuIHBvaW50IGluIHRpbWUuIFRoZXNlIGdhbWVzIHdlcmUgcGxheWVkIG9uIHRoZSAxIHZzLiAxIG9ubGluZSBsYWRkZXIgc3lzdGVtLCB3aGljaCBwbGFjZXMgcGxheWVycyBpbiBvbmUgb2Ygc2V2ZW4gImxlYWd1ZXMiIGFjY29yZGluZyB0byBhICJsYWRkZXIgcmFuayIgY2FsY3VsYXRlZCBieSBhIG1hdGNobWFraW5nIGFsZ29yaXRobS4gT25lJ3MgbGFkZGVyIHJhbmsgaXMgYmFzZWQgb24gaGlzIG9yIGhlciB3aW4tbG9zcyByZWNvcmQgYW5kIHRoZSBsYWRkZXIgcmFuayBvZiB0aGUgb3Bwb25lbnQgaW4gZWFjaCBnYW1lLiBXaGVuIGEgcGxheWVyIHdpc2hlcyB0byBwbGF5LCB0aGUgc3lzdGVtIGF0dGVtcHRzIHRvIGZpbmQgYW4gb3Bwb25lbnQgd2l0aCBhIHNpbWlsYXIgbGFkZGVyIHJhbmsuIE9uY2UgYSBwbGF5ZXIgYmVjb21lcyBza2lsbGVkIGVub3VnaCB0byBjb25zaXN0ZW50bHkgZGVmZWF0IG9wcG9uZW50cyB3aXRoaW4gdGhlIHNhbWUgbGVhZ3VlLCB0aGUgbWF0Y2htYWtpbmcgc3lzdGVtIHdpbGwgYmVnaW4gbWF0Y2hpbmcgdGhlIHBsYXllciBhZ2FpbnN0IG9wcG9uZW50cyBvZiB0aGUgbmV4dCBoaWdoZXN0IGxlYWd1ZS4gSWYgdGhlIHBsYXllciB3aW5zIGVub3VnaCBvZiB0aGVzZSBnYW1lcywgdGhlbiBoZSBvciBzaGUgd2lsbCBiZSBwcm9tb3RlZCB0byB0aGF0IGxlYWd1ZS4gU2ltaWxhcmx5LCBpZiBhIHBsYXllciBpcyBjb25zaXN0ZW50bHkgbG9zaW5nIGFnYWluc3Qgb3Bwb25lbnRzIHdpdGhpbiB0aGUgc2FtZSBsZWFndWUsIHRoZSBtYXRjaG1ha2luZyBzeXN0ZW0gd2lsbCBiZWdpbiBtYXRjaGluZyB0aGUgcGxheWVyIGFnYWluc3Qgb3Bwb25lbnRzIGluIHRoZSBuZXh0IGxvd2VzdCBsZWFndWUuIElmIHRoZSBwbGF5ZXIgbG9zZXMgZW5vdWdoIG9mIHRoZXNlIG1hdGNoZXMsIHRoZW4gaGUgb3Igc2hlIHdpbGwgYmUgZGVtb3RlZCB0byB0aGF0IGxlYWd1ZS4NCg0KDQohW0NoYW90aWMgc2NlbmVzIHN1Y2ggYXMgdGhpcyBvbmUgYXJlIHR5cGljYWwgb2YgYWxtb3N0IGV2ZXJ5IGdhbWUgb2YgU3RhcmNyYWZ0IElJXShpbnRyby5qcGcpDQoNCg0KPiBXaGF0IG1ha2VzIFNDMiBhbiBpbnRlcmVzdGluZyBnYW1lIHRvIHN0dWR5IGlzIHRoZSBmYWN0IHRoYXQgdGhlIHNoZWVyIGNvbXBsZXhpdHkgb2YgdGhlIGdhbWUgaW4gdGVybXMgb2YgYWxsIHRoZSB0aGluZ3MgdGhhdCBjb3VsZCBoYXBwZW4gYXQgYW55IGdpdmVuIG1vbWVudCBjb21wb3VuZGVkIGFjcm9zcyBhbiBlbnRpcmUgZ2FtZSBtZWFucyB0aGF0IGV2ZXJ5IGdhbWUgaXMgdW5pcXVlLiBFYWNoIGdhbWUgY2FuIHVuZm9sZCBpbiBhIHNlZW1pbmdseSBpbmZpbml0ZSBudW1iZXIgb2YgZGlmZmVyZW50IHdheXMuIFRoZSBwb3B1bGFyaXR5IG9mIFNDMiBoYXMgbGVkIHRvIGEgc3VzdGFpbmVkIHByb2Zlc3Npb25hbCBzY2VuZSwgd2hlcmUgcGxheWVycyBhcmUgc3BvbnNvcmVkIGJ5IHRlYW1zIGFuZCBjb21wZXRlIGluIHRvdXJuYW1lbnRzIGZvciBjYXNoIHByaXplcy4gVGhlc2UgdG91cm5hbWVudHMgY2FuIHRha2UgcGxhY2Ugb25saW5lIG9yIG9mZmxpbmUsIHdoZXJlIHBsYXllcnMgYXJlIGZsb3duIGludG8gYSBzdHVkaW8gb3IgYXJlbmEgdG8gY29tcGV0ZSBpbiBmcm9udCBvZiBhIGxpdmUgYXVkaWVuY2UuIFRoZXNlIGV2ZW50cyBhcmUgYWNjb21wYW5pZWQgYnkgY29tbWVudGF0b3JzIHdobyBzZXJ2ZSBhIHNpbWlsYXIgcm9sZSB0byB0aG9zZSBpbiB0cmFkaXRpb25hbCBzcG9ydHMuIEluIHRoZSBwYXN0LCB0aGUgZmlyc3QgcGxhY2UgcHJpemVzIGF0IHRoZSBtb3N0IHByZXN0aWdpb3VzIHRvdXJuYW1lbnRzIGhhdmUgcmVhY2hlZCAkMTAwLDAwMC4gVGhpcyBjb21wZXRpdGl2ZSBzY2VuZSBoYXMgcHJvdmlkZWQgdGhlIGdhbWUncyBwYXNzaW9uYXRlIGZhbmJhc2Ugd2l0aCBhIGNsZWFyIG1vdGl2YXRpb24gZm9yIGJlY29taW5nIGJldHRlciBhdCB0aGUgZ2FtZS4gVGhlIHByb2Zlc3Npb25hbCBwbGF5ZXJzIHNlcnZlIGFzIHJvbGUgbW9kZWxzIGZvciBob3cgdG8gYmVjb21lIGJldHRlciAoQSBiZWdpbm5lcidzIGd1aWRlIHRvIGNvbXBldGl0aXZlIFNDMiBjYW4gYmUgZm91bmQgW2hlcmVdKGh0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9SlNQUmdMNEQxbm8pKS4gQXMgYSBwYXNzaW9uYXRlIG1lbWJlciBvZiB0aGlzIGNvbW11bml0eSBkdXJpbmcgdGhlIHRpbWUgdGhhdCB0aGlzIGRhdGFzZXQgd2FzIGNvbXBpbGVkLCBJIG1hZGUgYSBzZXJpb3VzIChidXQgc2hvcnQtbGl2ZWQpIGF0dGVtcHQgdG8gaW1wcm92ZSBteSBTQzIgc2tpbGwgbGV2ZWwsIGR1cmluZyB3aGljaCBJIGFic29yYmVkIHZhcmlvdXMgYWR2aWNlIGFuZCBnZW5lcmFsbHkgYWNjZXB0ZWQgd2lzZG9tIG9uIGhvdyBvbmUgY2FuIGdldCBiZXR0ZXIgYXQgdGhlIGdhbWUsIGFzIHdlbGwgYXMgYSBkZWVwIGFwcHJlY2lhdGlvbiBmb3IgdGhlIHRhbGVudHMgb2YgcHJvZmVzc2lvbmFsIFNDMiBwbGF5ZXJzLiBXaXRoIHRoaXMgZGF0YXNldCwgSSB3b3VsZCBsaWtlIHRvIHNlZSBpZiB0aGVzZSBjb21tdW5pdHkgYmVzdCBwcmFjdGljZXMgYXJlIHN1cHBvcnRlZCBieSB0aGUgZGlmZmVyZW50aWF0aW9uIG9mIHNraWxsIGxldmVscyBiYXNlZCBvbiB0aGUgbGVhZ3VlIHBsYWNlbWVudCBvZiB0aGUgc3R1ZHkgcGFydGljaXBhbnRzLiBUaHJvdWdob3V0IHRoaXMgcHJvamVjdCwgSSBjb25zdWx0ZWQgd2l0aCBteSBmcmllbmQgd2hvIGludHJvZHVjZWQgbWUgdG8gdGhpcyBzY2VuZSBhbmQgd2hvIGlzIG1vcmUga25vd2xlZGdlYWJsZSBhbmQgc2tpbGxlZCB0aGFuIG1lIGF0IHRoaXMgZ2FtZS4gV2UgZGlzY3Vzc2VkIHdoYXQgb3BpbmlvbnMgdGhlIGNvbW11bml0eSBoYWQgb24gdGhlIGltcG9ydGFuY2Ugb2YgdmFyaW91cyBtZXRyaWNzIGFzIHdlbGwgYXMgaG93IHRvIGludGVycHJldCB0aGVzZSBtZXRyaWNzIGFuZCB3aGV0aGVyIG9yIG5vdCB0aGV5IGNvbnRhaW4gYW55IG5vbnNlbnNpY2FsIHZhbHVlcy4NCg0KDQo+IEF0IHRoZSBjb25jbHVzaW9uIG9mIHRoZWlyIHBhcGVyLCB0aGUgcmVzZWFyY2hlcnMgZGlzY3Vzc2VkIHRoZSBuZXh0IHN0dWR5IHRoYXQgdGhleSB3ZXJlIGVtYmFya2luZyBvbiwgd2hpY2ggaW52b2x2ZWQgYWNjZXB0aW5nIGxhcmdlIG51bWJlcnMgb2YgcmVwbGF5IGZpbGVzIGZyb20gZWFjaCBwYXJ0aWNpcGFudCBpbiBvcmRlciB0byBzZWFyY2ggZm9yIGNoYW5nZXMgaW4gc2tpbGwgbGV2ZWwgb3ZlciB0aW1lLiBUaG91Z2ggZWFjaCBkYXRhIHBvaW50IGluIHRoaXMgZGF0YXNldCByZXByZXNlbnRzIGEgc2luZ2xlIG1vbWVudCBpbiB0aW1lIGZvciBvbmUgcGxheWVyLCBJIHdhbnQgdG8gZXhwbG9yZSBob3cgaW4tZ2FtZSBtZXRyaWNzIGNoYW5nZSBhY3Jvc3MgYWdlIGdyb3VwcyBhbmQgZXhwZXJpZW5jZSBsZXZlbHMgYm90aCB3aXRoaW4gYW5kIGFjcm9zcyByYW5rcy4gSSBwbGFuIG9uIHVzaW5nIHZhcmlhYmxlcyBpbiB0aGUgZGF0YXNldCByZWxhdGVkIHRvIHRpbWUgdG8gc2VlIGlmIHRoZXJlIGFyZSBhbnkgZ2VuZXJhbCB0cmVuZHMgb3ZlciB0aW1lIHRoYXQgY2FuIGJlIGRpc2Nlcm5lZC4gVGhlIHJlc2VhcmNoZXJzIGFwcHJvYWNoZWQgdGhpcyBzdHVkeSBmcm9tIGEgY29nbml0aXZlIHNjaWVuY2UgcGVyc3BlY3RpdmUsIGludHJvZHVjaW5nIGEgbmV3IHVuaXQgb2YgbWVhc3VyZW1lbnQgY2FsbGVkIHRoZSBQZXJjZXB0aW9uLUFjdGlvbiBDeWNsZSAoUEFDKSwgd2hpY2ggYXR0ZW1wdHMgdG8gcXVhbnRpZnkgc2tpbGwgaW4gdGVybXMgb2YgdGhlIGFtb3VudCBvZiB0aW1lIGVsYXBzZWQgYmV0d2VlbiBhIHBsYXllciBwZXJjZWl2aW5nIGFuIGV2ZW50IGFuZCBhY3RpbmcgdXBvbiBpdCAoYSBtb3JlIGRldGFpbGVkIGV4cGxhbmF0aW9uIG9mIHRoZSBQQUMgY2FuIGJlIGZvdW5kIFtoZXJlXShodHRwczovL3d3dy55b3V0dWJlLmNvbS93YXRjaD92PWJuUlFNdjB4SWdnKSkuIFRoZXkgaHlwb3RoZXNpemVkIHRoYXQgdGhlIHZhcmlhYmxlcyB0aGF0IGFyZSBtb3N0IGNvcnJlbGF0ZWQgd2l0aCBza2lsbCBjaGFuZ2UgYXMgYSBwbGF5ZXIgaW1wcm92ZXMgYW5kIG1vdmVzIHVwIHRoZSByYW5rcyBvZiB0aGUgbGFkZGVyIGFuZCBzaG93ZWQgdGhhdCBtYWNoaW5lIGxlYXJuaW5nIG1ldGhvZHMgY2FuIGJlIHVzZWQgdG8gcHJlZGljdCBhIHBsYXllcidzIHJhbmsgYmFzZWQgb24gc3VjaCB2YXJpYWJsZXMuIEkgd2lzaCB0byBhdm9pZCB1c2luZyB2YXJpYWJsZXMgcmVsYXRlZCB0byBQQUMgc2luY2UgSSBkb24ndCBiZWxpZXZlIHRoYXQgSSBoYXZlIHRoZSBuZWNlc3NhcnkgY29nbml0aXZlIHNjaWVuY2UgYmFja2dyb3VuZCB0byB1bmRlcnN0YW5kIGhvdyB0aGV5IHdlcmUgZGVyaXZlZCBhbmQgd2hhdCB0aGV5IHNpZ25pZnkuIEFsc28sIEkgd2FudCB0byBhdm9pZCByZXRyZWFkaW5nIHRoZSBwYXRoIHRoZSByZXNlYXJjaGVycyB0b29rLCBzaW5jZSB0aGVpciBwYXBlciBmb2N1c2VkIGhlYXZpbHkgb24gUEFDIGFuZCBpdHMgcmVsYXRpb24gdG8gc2tpbGwgbGV2ZWwuDQoNCg0KIyMyLiBUZWFtDQoNCg0KPiBTaW5jZSBJIGFtIHdvcmtpbmcgYWxvbmUgZm9yIHRoaXMgcHJvamVjdCwgSSBhbSBwbGFubmluZyBvbiBleHBsb3JpbmcgYW5kIGFuYWx5emluZyB0aGlzIGRhdGFzZXQgd2l0aCB0aGUgZm9sbG93aW5nIGFnZW5kYToNCg0KDQoqKioNCg0KMS4gQ2hlY2sgdGhlIHNlbGYtcmVwb3J0ZWQgdmFyaWFibGVzIGZvciBub25zZW5zaWNhbC9taXNzaW5nL291dGxpZXIgdmFsdWVzIGFuZCBkZWNpZGUgb24gYSB3YXkgdG8gZGVhbCB3aXRoIHRoZW0uDQoyLiBBbmFseXplIHRoZSB0aHJlZSBzZWxmLXJlcG9ydGVkIHRpbWUgdmFyaWFibGVzIG5vdCBkZXJpdmVkIGZyb20gdGhlIHJlcGxheSBmaWxlcywgKlRvdGFsSG91cnMqLCAqSG91cnNQZXJXZWVrKiwgYW5kICpBZ2UqLg0KMy4gUHJvcG9zZSBhbiBhcHByb3hpbWF0ZSBvcmRlciBvZiBpbXBvcnRhbmNlIGFtb25nIHRoZSBpbi1nYW1lIG1ldHJpY3MgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQgKGluIHRlcm1zIG9mIHdoaWNoIHNraWxscyB0aGUgU0MyIGNvbW11bml0eSBkZWVtcyBlc3NlbnRpYWwgZm9yIGluY3JlYXNpbmcgc2tpbGwgbGV2ZWwgYXMgb3Bwb3NlZCB0byB3aGF0J3MgIm5pY2UgdG8gaGF2ZSIsIHNpbWlsYXIgdG8gTWFzbG93J3MgaGllcmFyY2h5IG9mIG5lZWRzKS4NCjQuIEV4cGxvcmUgdGhlc2UgdmFyaWFibGVzIGluZGl2aWR1YWxseSBpbiB0aGUgb3JkZXIgcHJvcG9zZWQuDQo1LiBWaXN1YWxpemUgdGhlc2UgdmFyaWFibGVzIHRvZ2V0aGVyIHdpdGggdGhlIHRpbWUgdmFyaWFibGVzIHRvIGdldCBhbiBpbXByZWNpc2UgaWRlYSBvZiBob3cgdGhlc2UgbWV0cmljcyBjaGFuZ2VzIG92ZXIgdGltZS4NCjYuIENyZWF0ZSBtdWx0aXZhcmlhdGUgcGxvdHMgdG8gZmluZCBwb3NzaWJsZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gZGlmZmVyZW50IG1ldHJpY3MgYWNyb3NzIGRpZmZlcmVudCBsZWFndWVzIGFuZCBleHBlcmllbmNlIGxldmVscy4NCg0KKioqDQoNCg0KIyMzLiBBbmFseXNpcyBvZiBEYXRhIFF1YWxpdHkNCg0KDQo+IFRoZSBkYXRhc2V0IHVzZWQgZm9yIHRoaXMgYW5hbHlzaXMgb3JpZ2luYXRlcyBmcm9tIHRoZSByZXNlYXJjaCBwYXBlciBbVmlkZW8gR2FtZSBUZWxlbWV0cnkgYXMgYSBDcml0aWNhbCBUb29sIGluIHRoZSBTdHVkeSBvZiBDb21wbGV4IFNraWxsIExlYXJuaW5nXShodHRwOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc29uZS9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wb25lLjAwNzUxMjkjcG9uZS0wMDc1MTI5LWcwMDEpIChUaG9tcHNvbiBldCBhbCwgMjAxMykgYW5kIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cDovL3N1bW1pdC5zZnUuY2EvaXRlbS8xMzMyOCkuIEZyb20gQXVndXN0IDEydGgsIDIwMTEgdG8gU2VwdGVtYmVyIDE5dGgsIDIwMTEsIHRoZSByZXNlYXJjaGVycyBzb2xpY2l0ZWQgbWVtYmVycyBvZiB2YXJpb3VzIG9ubGluZSBTQzIgY29tbXVuaXR5IGh1YnMgZm9yIHRoZWlyIGdhbWUgcmVwbGF5IGZpbGVzLCBmcm9tIHdoaWNoIEkgZm91bmQgb3V0IGFib3V0IHRoaXMgc3R1ZHkuIENvbnRyaWJ1dG9ycyB3ZXJlIGFsc28gYXNrZWQgdG8gY29tcGxldGUgYSBzdXJ2ZXkgdGhhdCBhc2tlZCBhYm91dCB0aGVpciBhZ2UsIHNraWxsIGxldmVsLCBhbmQgcGxheWluZyBoYWJpdHMuIFNpbmNlIGVhY2ggcmVwbGF5IGZpbGUgb25seSBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCB3aGF0IGhhcHBlbnMgZHVyaW5nIHRoZSBjb3Vyc2Ugb2YgdGhlIG9uZSBnYW1lIHRoYXQgdGhlIGZpbGUgb3JpZ2luYXRlZCBmcm9tLCBpdCBpcyB1bmFibGUgdG8gcHJvdmlkZSBwbGF5ZXJzJyBwZXJzb25hbCBkZXRhaWxzIHN1Y2ggYXMgYWdlIG9yIGhvdyBsb25nIHRoZXkndmUgYmVlbiBwbGF5aW5nIHRoZSBnYW1lLiBBZGRpdGlvbmFsbHksIGEgcGxheWVyJ3MgbGVhZ3VlIGlzbid0IGluY2x1ZGVkIHNpbmNlIHRoYXQgaW5mb3JtYXRpb24gZG9lc24ndCBkaXJlY3RseSBhZmZlY3QgdGhlIGdhbWUgb25jZSB0aGUgbWF0Y2htYWtpbmcgc3lzdGVtIGlzIGRvbmUgZmluZGluZyBhbiBvcHBvbmVudC4gVGhlIGxlYWd1ZSBuYW1lcywgaW4gb3JkZXIgb2YgaW5jcmVhc2luZyBza2lsbCwgYXJlIEJyb256ZSwgU2lsdmVyLCBHb2xkLCBQbGF0aW51bSwgRGlhbW9uZCwgTWFzdGVyLCBhbmQgR3JhbmRNYXN0ZXIuIERhdGEgcG9pbnRzIHdpdGggYSAqTGVhZ3VlSW5kZXgqIHZhbHVlIG9mIDggaW5kaWNhdGUgcmVwbGF5IGZpbGVzIG9mIHByb2Zlc3Npb25hbCBnYW1lcyB0aGF0IHdlcmUgc2hhcmVkIHdpdGggdGhlIHB1YmxpYy4gVGhlc2UgcmVwbGF5cyB3ZXJlIHN1YnNlcXVlbnRseSBzdWJtaXR0ZWQgdG8gdGhlIHN0dWR5IGJ5IGNvbW11bml0eSBtZW1iZXJzLCB3aGljaCBpcyB3aHkgdGhlc2UgcmVwbGF5cyBhbGwgaGF2ZSBtaXNzaW5nIHZhbHVlcyBmb3Igc2VsZi1yZXBvcnRlZCB2YXJpYWJsZXMuDQoNCg0KPiBJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCAicHJvZmVzc2lvbmFsIiBzaW1wbHkgbWVhbnMgdGhhdCB0aG9zZSByZXBsYXlzIGFyZSBvZiBnYW1lcyB0aGF0IHdlcmUgcGxheWVkIGJ5IHByb2Zlc3Npb25hbCBwbGF5ZXJzIGluIHRvdXJuYW1lbnRzLiBUaGVyZSBpcyBubyAicHJvZmVzc2lvbmFsIiBsZWFndWUgaW4gdGhlIGxhZGRlciBzeXN0ZW0uIEFsc28sIHRoZSBHcmFuZE1hc3RlciBMZWFndWUgaXMgbGltaXRlZCB0byB0aGUgdG9wIDIwMCBwbGF5ZXJzLiBJdCBpcyBuZWNlc3NhcnkgZm9yIGEgcGxheWVyIHRvIGJlIHN1Y2Nlc3NmdWwgaW4gdGhlIEdyYW5kTWFzdGVyIExlYWd1ZSBpbiBvcmRlciB0byBwbGF5IFNDMiBwcm9mZXNzaW9uYWxseSwgc28gdGhlcmUgaXMgYSBjaGFuY2UgdGhhdCBwcm9mZXNzaW9uYWwgcGxheWVycyBjYW4gYmUgcmVwcmVzZW50ZWQgaW4gYm90aCBsZWFndWVzIGluIHRoaXMgZGF0YXNldC4gSG93ZXZlciwgdGhpcyBpcyB1bmxpa2VseSBkdWUgdG8gdGhlaXIgdGVuZGVuY3kgdG8gaGlkZSB0aGVpciBpZGVudGl0aWVzIHdoZW4gcGxheWluZyBvbmxpbmUgYXMgbXVjaCBhcyBwb3NzaWJsZS4gVGhpcyBpcyBzbyB0aGF0IHRoZWlyIHJpdmFscyBkb24ndCBnZXQgb3Bwb3J0dW5pdGllcyB0byBzdHVkeSB0aGVtLiBUaGVyZWZvcmUsIGl0IGlzIGhpZ2hseSB1bmxpa2VseSB0aGF0IGEgcHJvZmVzc2lvbmFsIHBsYXllciBwYXJ0aWNpcGF0ZWQgaW4gdGhpcyBzdXJ2ZXkgYW5kIHdpbGxpbmdseSBzdWJtaXR0ZWQgaGlzIG93biByZXBsYXlzIG9mIEdyYW5kTWFzdGVyIExlYWd1ZSBnYW1lcyBwbGF5ZWQgb25saW5lIHRvIHRoaXMgZGF0YXNldC4gRHVlIHRvIHRoZSBzbWFsbCBzYW1wbGUgc2l6ZSBvZiBwcm9mZXNzaW9uYWwgcmVwbGF5cyBpbiB0aGUgZGF0YXNldCAoNTUvMzM5NSkgYW5kIHRoZSBsYWNrIG9mIGFjY29tcGFueWluZyBwbGF5ZXIgaW5mb3JtYXRpb24gSSBkZWNpZGVkIHRvIHJlbW92ZSB0aGVzZSByZXBsYXlzIGZyb20gdGhlIGRhdGFzdC4gDQoNCg0KPiBNeSBhbmFseXNpcyBiZWdpbnMgd2l0aCBsb2FkaW5nIHRoZSBjc3YgZmlsZSBjb250YWluaW5nIHRoZSBkYXRhc2V0IGFuZCBjb252ZXJ0aW5nIGludGVnZXIgdmFyaWFibGVzIHRoYXQgd2VyZSBpbXBvcnRlZCBhcyBmYWN0b3JzLiBJIHRoZW4gY3JlYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCAqcmFuayogdGhhdCBhZGRzIHRoZSBwcm9wZXIgbmFtZSBmb3IgZWFjaCBwbGF5ZXIncyBsZWFndWUgYmFzZWQgb24gdGhlICpMZWFndWVJbmRleCogdmFyaWFibGUuIFdoaWxlIHRoZSB2YXJpYWJsZXMgZGVyaXZlZCBmcm9tIHRoZSByZXBsYXkgZmlsZXMgYXJlIGluaGVyZW50bHkgYWNjdXJhdGUsIHRoZSB2YXJpYWJsZXMgZGVyaXZlZCBmcm9tIHN1cnZleSBhbnN3ZXJzIHNob3VsZCBiZSBjaGVja2VkIGZvciBpbmFjY3VyYWNpZXMgYW5kIGlycmVndWxhcml0aWVzLiANCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0Kc2MgPC0gcmVhZC5jc3YoJ1N0YXJDcmFmdDJSZXBsYXlBbmFseXNpcy0xLmNzdicpDQoNCiNSZW1vdmUgcHJvZmVzc2lvbmFsIHJlcGxheXMNCg0Kc2MgPC0gZmlsdGVyKHNjLCBzYyRMZWFndWVJbmRleCAhPSA4KQ0KDQojQ29udmVydCBmYWN0b3JzIGJhY2sgdG8gaW50ZWdlcnMNCiNBdXRvbWF0aWNhbGx5IHJlcGxhY2UgIk5VTEwiIHN0cmluZyB2YWx1ZXMgd2l0aCAiTkEiDQoNCnNjJFRvdGFsSG91cnMgPC0gYXMuaW50ZWdlcihhcy5jaGFyYWN0ZXIoc2MkVG90YWxIb3VycykpDQpzYyRIb3Vyc1BlcldlZWsgPC0gYXMuaW50ZWdlcihhcy5jaGFyYWN0ZXIoc2MkSG91cnNQZXJXZWVrKSkNCnNjJEFnZSA8LSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3RlcihzYyRBZ2UpKQ0KDQojQWRkIGEgbmV3IHZhcmlhYmxlICJMZWFndWUiIHNvIHRoYXQgdGhlIHZhbHVlcyBpbiB0aGUgaW5jbHVkZWQgIkxlYWd1ZUluZGV4IiB2YXJpYWJsZSBhcmUgYXNzb2NpYXRlZCB3aXRoIHRoZSBuYW1lcyBvZiBlYWNoIGxlYWd1ZQ0KDQpyYW5rIDwtIGMoJ0Jyb256ZScsICdTaWx2ZXInLCAnR29sZCcsICdQbGF0aW51bScsICdEaWFtb25kJywgJ01hc3RlcicsICdHcmFuZE1hc3RlcicpDQpyYW5rIDwtIGZhY3RvcihyYW5rLCBsZXZlbHMgPSBjKCdCcm9uemUnLCAnU2lsdmVyJywgJ0dvbGQnLCAnUGxhdGludW0nLCAnRGlhbW9uZCcsICdNYXN0ZXInLCAnR3JhbmRNYXN0ZXInKSkNCnNjIDwtIG11dGF0ZShzYywgTGVhZ3VlID0gcmFua1tMZWFndWVJbmRleF0pDQoNCmBgYA0KDQoNCiMjIypIb3Vyc1BlcldlZWsqIGFuZCAqVG90YWxIb3VycyoNCg0KDQo+ICBTaW5jZSBteSBhbmFseXNpcyB3aWxsIHBhcnRseSBiZSBmb2N1c2VkIG9uIHRoZSBjaGFuZ2UgaW4gcGxheWVyIG1ldHJpY3MgYXMgYSBmdW5jdGlvbiBvZiB0aW1lLCBpdCB3b3VsZCBiZSBhcHByb3ByaWF0ZSB0byBzdGFydCB3aXRoIHRoZSB0d28gdGltZSB2YXJpYWJsZXMsICpIb3Vyc1BlcldlZWsqIGFuZCAqIipUb3RhbEhvdXJzKiIqLiBUaGVzZSB2YXJpYWJsZXMgYXJlIHByb25lIHRvIHJlcG9ydGluZyBpbmFjY3VyYWN5IGR1ZSB0byB0aGUgZmFjdCB0aGF0IHBhcnRpY2lwYW50cyBhcmUgYXR0ZW1wdGluZyB0byBnZW5lcmFsaXplIHNldmVyYWwgbW9udGhzIG9mIHBsYXlpbmcgZXhwZXJpZW5jZSBhbmQgaGFiaXRzIGludG8gdHdvIG51bWJlcnMuIEJ5IHJlbW92aW5nIG91dGxpZXIgdmFsdWVzIGFuZCBkcmF3aW5nIHNjYXR0ZXJwbG90cywgaXQgaXMgYWxzbyBvYnZpb3VzIHRoYXQgdGhlIHJlcG9ydGVkIHZhbHVlcyBmb3IgYm90aCB2YXJpYWJsZXMgaGF2ZSBiZWVuIHJvdW5kZWQuIEZvciAqVG90YWxIb3VycyosIHRoZSByb3VuZGluZyBsb29rcyB0byBoYXZlIGJlZW4gZG9uZSBieSB0aGUgcmVzcG9uZGVudHMgc2luY2UgdGhlcmUgYXJlIGRhdGEgcG9pbnRzIHNjYXR0ZXJlZCBiZXR3ZWVuIHJvdW5kZWQgdmFsdWVzLiBGb3IgKkhvdXJzUGVyV2VlayosIEkgYXNzdW1lIHRoYXQgcmVzcG9uZGVudHMgY291bGQgb25seSBjaG9vc2UgZnJvbSBjZXJ0YWluICB2YWx1ZXMgcHJlc2VsZWN0ZWQgYnkgdGhlIHJlc2VhcmNoZXJzIHJhdGhlciB0aGFuIHR5cGluZyB0aGVpciBhbnN3ZXIgbWFudWFsbHkuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkSG91cnNQZXJXZWVrKSkgKyBnZW9tX2RlbnNpdHkoYncgPSAyLjUsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAuNSkgKyBsYWJzKHggPSAnUmVwb3J0ZWQgSG91cnMgUGxheWVkIFBlciBXZWVrJywgdGl0bGUgPSAnSG91cnNQZXJXZWVrIE91dGxpZXJzIEZlbmNlZCcpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkNCg0KZ2dwbG90KHNjLCBhZXMoc2MkSG91cnNQZXJXZWVrKSkgKyBnZW9tX2RlbnNpdHkoYncgPSAyLjUsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAuNSkgKyBsYWJzKHggPSAnUmVwb3J0ZWQgSG91cnMgUGxheWVkIFBlciBXZWVrJywgdGl0bGUgPSAnSG91cnNQZXJXZWVrIE91dGxpZXJzIFJlbW92ZWQnKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpzdW1tYXJ5KHNjJEhvdXJzUGVyV2VlaykNCnN1bW1hcnkoc2MkVG90YWxIb3VycykNCg0KYGBgDQoNCg0KPiBXaGF0IGltbWVkaWF0ZWx5IHN0YW5kcyBvdXQgaW4gdGhlIHN1bW1hcnkgc3RhdGlzdGljcyBhbmQgdGhlIGRlbnNpdHkgcGxvdHMgYXJlIHRoZSBtYXhpbXVtIHZhbHVlcyBmb3IgYm90aCB2YXJpYWJsZXMuIFRoZSBtYXhpbXVtIHZhbHVlIGZvciAqSG91cnNQZXJXZWVrKiBjb3JyZXNwb25kcyB0byAyNCBob3VycyBwZXIgZGF5IG9mIHBsYXlpbmcgdGltZSBldmVyeSB3ZWVrLCB3aGlsZSB0aGUgbWF4aW11bSByZXBvcnRlZCB2YWx1ZSBvZiAxLDAwMCwwMDAgZm9yICpUb3RhbEhvdXJzKiBmYXIgZXhjZWVkcyB0aGUgbnVtYmVyIG9mIGhvdXJzIHRoYXQgaGFkIGVsYXBzZWQgc2luY2UgU0MyIHdhcyByZWxlYXNlZCB0byB0aGUgdGltZSB0aGF0IHRoZSBkYXRhIGNvbGxlY3Rpb24gcGVyaW9kIGNvbmNsdWRlZC4gV2hlbiBJIGRpc2N1c3NlZCB0aGlzIG1hdHRlciB3aXRoIFByb2Zlc3NvciBSb2JiaW5zLCBvbmUgb2YgaGVyIHN1Z2dlc3Rpb25zIHdhcyB0byAiZmVuY2UiIHRoZSB2YWx1ZXMgc3VjaCB0aGF0IG91dGxpZXJzIGFyZSByZXBsYWNlZCB3aXRoIGEgc2Vuc2libGUgY2VpbGluZyB2YWx1ZS4gRnJvbSBpbnRlcnZpZXdzIG9mIHByb2Zlc3Npb25hbCBwbGF5ZXJzIGFzIHdlbGwgYXMgaGVhcnNheSBmcm9tIHRob3NlIHdobyBrZWVwIGluIGNvbnRhY3Qgd2l0aCB0aGVtLCBJIGNob3NlIDk4IGhvdXJzIHBsYXllZCBwZXIgd2VlayBhcyBhIGNlaWxpbmcgdmFsdWUuIFRoaXMgY29ycmVzcG9uZHMgdG8gMTQgaG91cnMgcGxheWVkIHBlciBkYXksIHdoaWNoIGlzIGEgaGFiaXQgdGhhdCBjb3VsZCBtb3N0IGxpa2VseSBvbmx5IGJlIHN1c3RhaW5lZCBieSBwcm9mZXNzaW9uYWwgcGxheWVycyBzaWduZWQgdG8gYSBtYWpvciB0ZWFtLiBTdWNoIHRlYW1zIG9mdGVuIG9wZXJhdGUgYSAidGVhbSBob3VzZSIgd2hlcmUgYWxsIHRoZSBwbGF5ZXJzIGxpdmUgYW5kIHRyYWluIHRvZ2V0aGVyLiBJdCBpcyBub3QgdW5jb21tb24gZm9yIHRoZSBzcG9uc29ycyB0byBoaXJlIG1haWRzIHRvIGNvb2sgYW5kIGNsZWFuIHNvIHRoYXQgdGhlIHBsYXllcnMgY2FuIGZvY3VzIGFsbCB0aGVpciBhdHRlbnRpb24gb24gcGxheWluZyBTQzIuIEluIHN1Y2ggY2FzZXMsIGl0IGlzIG5vdCB1bnVzdWFsIGZvciBwbGF5ZXJzIHRvIGRldm90ZSAxMC0xNCBob3VycyBwZXIgZGF5IHRvIHByYWN0aWNpbmcgZm9yIHVwY29taW5nIGltcG9ydGFudCBtYXRjaGVzLg0KDQoNCj4gQW5vdGhlciBzdWdnZXN0aW9uIHdhcyB0byBzaW1wbHkgcmVtb3ZlIHRoZXNlIG91dGxpZXJzIGZyb20gdGhlIGRhdGFzZXQsIHNpbmNlIHRoZSByZXNwb25kZW50cyBtaWdodCBub3QgaGF2ZSBnaXZlbiBhIHNlcmlvdXMgYW5zd2VyIHRvIHRoZSBxdWVzdGlvbi4gV2hlbiBib3RoIG1ldGhvZHMgYXJlIGNvbXBhcmVkIHNpZGUgYnkgc2lkZSBiZWxvdywgdGhlcmUgZG9lc24ndCBzZWVtIHRvIGJlIGFueSBhcHByZWNpYWJsZSBkaWZmZXJlbmNlIGR1ZSB0byB0aGUgc21hbGwgbnVtYmVyIG9mIG91dGxpZXJzICg1NiBvdXQgb2YgMywzMzkpLiBBcyBmb3IgdGhlIHNpbmdsZSBkYXRhIHBvaW50IHdpdGggYSB2YWx1ZSBvZiAwIGhvdXJzIHBlciB3ZWVrLCBJIGludGVycHJldGVkIHRoYXQgYXMgdGhlIHJlc3BvbmRlbnQgcGxheWluZyB0aGUgZ2FtZSB2ZXJ5IGluZnJlcXVlbnRseS4gQmVjYXVzZSB0aGVyZSB3YXMgb25seSBvbmUgc3VjaCBkYXRhIHBvaW50IGFuZCB0aGUgZmFjdCB0aGF0IHRoZSBuZXh0IGNsb3Nlc3QgdmFsdWUgaXMgMiwgSSBkZWNpZGVkIG5vdCB0byByZW1vdmUgaXQuIA0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiNSZXN0cmljdCAiSG91cnNQZXJXZWVrIiB0byBhIG1heGltdW0gdmFsdWUgb2YgOTgNCg0Kc2NfcmVzdHJpY3QgPC0gc2MNCnNjX3Jlc3RyaWN0JEhvdXJzUGVyV2VlayA8LSBpZmVsc2Uoc2NfcmVzdHJpY3QkSG91cnNQZXJXZWVrID4gOTgsIDk4LCBzY19yZXN0cmljdCRIb3Vyc1BlcldlZWspDQoNCmdncGxvdChzY19yZXN0cmljdCwgYWVzKHNjX3Jlc3RyaWN0JEhvdXJzUGVyV2VlaykpICsgZ2VvbV9kZW5zaXR5KGJ3ID0gMi41LCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gLjUpICsgbGFicyh4ID0gJ1JlcG9ydGVkIEhvdXJzIFBsYXllZCBQZXIgV2VlaycsIHRpdGxlID0gJ0hvdXJzUGVyV2VlayBPdXRsaWVycyBGZW5jZWQnKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCAxMDAsIGJ5ID0gNSkpKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpDQoNCiNSZW1vdmUgZGF0YSB3aXRoICJIb3Vyc1BlcldlZWsiIHZhbHVlcyBncmVhdGVyIHRoYW4gOTgNCg0Kc2NfcmVtb3ZlIDwtIHNjDQpzY19yZW1vdmUgPC0gZmlsdGVyKHNjLCBIb3Vyc1BlcldlZWsgPD0gOTgpDQoNCnN1bW1hcnkoc2NfcmVzdHJpY3QkSG91cnNQZXJXZWVrKQ0Kc3VtbWFyeShzY19yZW1vdmUkSG91cnNQZXJXZWVrKQ0KDQpnZ3Bsb3Qoc2NfcmVtb3ZlLCBhZXMoc2NfcmVtb3ZlJEhvdXJzUGVyV2VlaykpICsgZ2VvbV9kZW5zaXR5KGJ3ID0gMi41LCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gLjUpICsgbGFicyh4ID0gJ1JlcG9ydGVkIEhvdXJzIFBsYXllZCBQZXIgV2VlaycsIHRpdGxlID0gJ0hvdXJzUGVyV2VlayBPdXRsaWVycyBSZW1vdmVkJykgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgMTAwLCBieSA9IDUpKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKQ0KDQpgYGANCg0KDQo+IFRoaXMgdmFyaWFibGUgbG9va3MgdG8gYmUgdW5pbW9kYWwgYWZ0ZXIgYWRqdXN0aW5nIHRoZSBiYW5kd2lkdGggb2YgdGhlIGRlbnNpdHkgcGxvdCwgd2l0aCBhIG1vZGUgb2YgYXBwcm94aW1hdGVseSA3IGFuZCBhIG1lYW4gb2YgMTUuODcuIFRoZSBtZWFuIHZhbHVlIGNvcnJlc3BvbmRzIHRvIGFyb3VuZCAyIGhvdXJzIHBlciBkYXkgb2YgcGxheSwgd2hpY2ggc291bmRzIHJlYXNvbmFibGUgZm9yIHRoYXQgdGltZSBwZXJpb2QsIGR1cmluZyB3aGljaCB0aGUgZ2FtZSB3YXMgc3RpbGwgcmVsYXRpdmVseSBuZXcgYW5kIHRoZSBmYW5iYXNlIHdhcyBhdCBpdHMgbGFyZ2VzdC4gVGhlIGRpc3RyaWJ1dGlvbiBpcyByaWdodC1za2V3ZWQsIHdoaWNoIGdpdmVzIGFuIGlkZWEgb2YgaG93IG1hbnkgJ3R5cGljYWwnIHBsYXllcnMgdGhlcmUgYXJlIGNvbXBhcmVkIHRvIHRoZSBtb3JlIGRlZGljYXRlZCBvbmVzIA0KDQoNCj4gU2ltaWxhcmx5LCBJIHRyaWVkIGJvdGggcmVzdHJpY3RpbmcgdGhlIG1heGltdW0gdmFsdWUgZm9yICpUb3RhbEhvdXJzKiB0byA4LDEwNiBhbmQgcmVtb3ZpbmcgZGF0YSBwb2ludHMgdGhhdCBleGNlZWQgdGhhdCB2YWx1ZS4gSSBhcnJpdmVkIGF0IHRoaXMgdGhlb3JldGljYWwgbWF4aW11bSBieSBjb3VudGluZyB0aGUgbnVtYmVyIG9mIGRheXMgZnJvbSB0aGUgZWFybGllc3QgZGF0ZSB0aGF0IGEgbWVtYmVyIG9mIHRoZSBnZW5lcmFsIHB1YmxpYyBjb3VsZCBiZWdpbiBwbGF5aW5nIHRoZSBnYW1lIGNvbnNpc3RlbnRseSAodGhlIHByZS1yZWxlYXNlIGJldGEgdGVzdCBwZXJpb2QgYmVnYW4gb24gRmVicnVhcnkgMTd0aCwgMjAxMCkgdG8gdGhlIGVuZCBvZiB0aGUgZGF0YSBjb2xsZWN0aW9uIHBlcmlvZCwgU2VwdGVtYmVyIDE5dGgsIDIwMTEuIEFzc3VtaW5nIHRoYXQgYW4gZXh0cmVtZWx5IGRlZGljYXRlZCBmYW4gcGxheWVkIHRoZSB0aGVvcmV0aWNhbCBtYXhpbXVtIG9mIDE0IGhvdXJzIHBlciBkYXkgZXZlcnkgZGF5IGR1cmluZyB0aGF0IHBlcmlvZCwgaGUgb3Igc2hlIHdpbGwgaGF2ZSBwbGF5ZWQgdGhlIGdhbWUgZm9yIGEgdG90YWwgb2YgOCwxMDYgaG91cnMuIA0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiNSZXN0cmljdCAiVG90YWxIb3VycyIgdG8gYSBtYXhpbXVtIHZhbHVlIG9mIDgsMTA2DQoNCnNjX3Jlc3RyaWN0JFRvdGFsSG91cnMgPC0gaWZlbHNlKHNjX3Jlc3RyaWN0JFRvdGFsSG91cnMgPiA4MTA2LCA4MTA2LCBzY19yZXN0cmljdCRUb3RhbEhvdXJzKQ0Kc3VtbWFyeShzY19yZXN0cmljdCRUb3RhbEhvdXJzKQ0KDQpnZ3Bsb3Qoc2NfcmVzdHJpY3QsIGFlcyhzY19yZXN0cmljdCRUb3RhbEhvdXJzKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDIwMCwgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IC41KSArIGxhYnMoeCA9ICdSZXBvcnRlZCBUb3RhbCBOdW1iZXIgb2YgSG91cnMgUGxheWVkJywgdGl0bGUgPSAnVG90YWxIb3VycyBPdXRsaWVycyBSZXN0cmljdGVkJykgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKQ0KDQojUmVtb3ZlIGRhdGEgd2l0aCAiVG90YWxIb3VycyIgdmFsdWVzIGdyZWF0ZXIgdGhhbiA4LDEwNg0KDQpzY19yZW1vdmUgPC0gZmlsdGVyKHNjX3JlbW92ZSwgVG90YWxIb3VycyA8PSA4MTA2KQ0Kc3VtbWFyeShzY19yZW1vdmUkVG90YWxIb3VycykNCg0KDQpnZ3Bsb3Qoc2NfcmVtb3ZlLCBhZXMoc2NfcmVtb3ZlJFRvdGFsSG91cnMpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMjAwLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gLjUpICsgbGFicyh4ID0gJ1JlcG9ydGVkIFRvdGFsIE51bWJlciBvZiBIb3VycyBQbGF5ZWQnLCB0aXRsZSA9ICdUb3RhbEhvdXJzIE91dGxpZXJzIFJlbW92ZWQnKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpDQoNCmBgYA0KDQoNCj4gQWdhaW4sIGJvdGggbWV0aG9kcyBmb3IgZGVhbGluZyB3aXRoIG91dGxpZXJzIHlpZWxkIGFsbW9zdCBpZGVudGljYWwgZGlzdHJpYnV0aW9ucy4gSW4gdGhlIGFib3ZlIGhpc3RvZ3JhbXMsIHNldmVyYWwgb3V0bGllciB2YWx1ZXMgdGhhdCBhcmUgd2VsbCBiZWxvdyA4LDEwNiBob3VycyBhbmQgeWV0IHdlbGwgYWJvdmUgdGhlIG1lZGlhbiBvZiA1MDAgY2FuIGJlIHNlZW4uIFN1c3RhaW5pbmcgYSAxNCBob3VyIHBlciBkYXkgU0MyIGhhYml0IGluIGFkZGl0aW9uIHRvIHNsZWVwIHRpbWUgYW5kIGFueSBwb3NzaWJsZSBzY2hvb2wgYW5kIHdvcmsgcmVzcG9uc2liaWxpdGllcyB3b3VsZCBiZSBxdWl0ZSBkaWZmaWN1bHQuIEV2ZW4gYSBwcm9mZXNzaW9uYWwgcGxheWVyIHdvdWxkIGhhdmUgdHJvdWJsZSBrZWVwaW5nIHVwIHdpdGggdGhhdCBsZXZlbCBvZiBwcmFjdGljZSBmb3IgYW4gZXh0ZW5kZWQgcGVyaW9kIG9mIHRpbWUgd2l0aG91dCBmZWVsaW5nIG1lbnRhbCBleGhhdXN0aW9uIG9yIGV4cGVyaWVuY2luZyB3cmlzdCBpbmp1cmllcy4gVGhlcmVmb3JlLCBJIGRlY2lkZWQgdG8gZnVydGhlciByZXN0cmljdCB0aGlzIHZhcmlhYmxlIHRvIGEgbWF4aW11bSB2YWx1ZSBvZiA0LDAwMCBob3Vycywgd2hpY2ggY29ycmVzcG9uZHMgdG8gYXBwcm94aW1hdGVseSA3IGhvdXJzIHBlciBkYXkuIA0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiNGdXJ0aGVyIHJlc3RyaWN0ICJUb3RhbEhvdXJzIiB0byBhIG1heGltdW0gdmFsdWUgb2YgNCwwMDANCg0Kc2NfcmVzdHJpY3QkVG90YWxIb3VycyA8LSBpZmVsc2Uoc2NfcmVzdHJpY3QkVG90YWxIb3VycyA+IDQwMDAsIDQwMDAsIHNjX3Jlc3RyaWN0JFRvdGFsSG91cnMpDQpzdW1tYXJ5KHNjX3Jlc3RyaWN0JFRvdGFsSG91cnMpDQoNCmdncGxvdCgpICsgZ2VvbV9oaXN0b2dyYW0oZGF0YSA9IHNjX3Jlc3RyaWN0LCBhZXMoeCA9IHNjX3Jlc3RyaWN0JFRvdGFsSG91cnMpLCBiaW53aWR0aCA9IDIwMCwgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IC41KSArIGxhYnMoeCA9ICdSZXBvcnRlZCBUb3RhbCBOdW1iZXIgb2YgSG91cnMgUGxheWVkJywgdGl0bGUgPSAnVG90YWxIb3VycyBPdXRsaWVycyBSZXN0cmljdGVkJykgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgNDAwMCwgMjUwKSkpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkNCg0KZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHkoZGF0YSA9IHNjX3Jlc3RyaWN0LCBhZXMoeCA9IHNjX3Jlc3RyaWN0JFRvdGFsSG91cnMpLCBidyA9IDc1LCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gLjUpICsgbGFicyh4ID0gJ1JlcG9ydGVkIFRvdGFsIE51bWJlciBvZiBIb3VycyBQbGF5ZWQnLCB0aXRsZSA9ICdUb3RhbEhvdXJzIE91dGxpZXJzIFJlc3RyaWN0ZWQnKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCA0MDAwLCAyNTApKSkgICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkNCg0KI0Z1cnRoZXIgcmVtb3ZlIGRhdGEgd2l0aCAiVG90YWxIb3VycyIgdmFsdWVzIGdyZWF0ZXIgdGhhbiA0LDAwMA0KDQoNCnNjX3JlbW92ZSA8LSBmaWx0ZXIoc2NfcmVtb3ZlLCBUb3RhbEhvdXJzIDw9IDQwMDApDQpzdW1tYXJ5KHNjX3JlbW92ZSRUb3RhbEhvdXJzKQ0KDQpnZ3Bsb3QoKSArIGdlb21faGlzdG9ncmFtKGRhdGEgPSBzY19yZW1vdmUsIGFlcyh4ID0gc2NfcmVtb3ZlJFRvdGFsSG91cnMpLCBiaW53aWR0aCA9IDIwMCwgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IC41KSArIGxhYnMoeCA9ICdSZXBvcnRlZCBUb3RhbCBOdW1iZXIgb2YgSG91cnMgUGxheWVkJywgdGl0bGUgPSAnVG90YWxIb3VycyBPdXRsaWVycyBSZW1vdmVkJykgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgNDAwMCwgMjUwKSkpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkNCg0KZ2dwbG90KCkgKyBnZW9tX2RlbnNpdHkoZGF0YSA9IHNjX3JlbW92ZSwgYWVzKHggPSBzY19yZW1vdmUkVG90YWxIb3VycyksIGJ3ID0gNzUsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAuNSkgKyBsYWJzKHggPSAnUmVwb3J0ZWQgVG90YWwgTnVtYmVyIG9mIEhvdXJzIFBsYXllZCcsIHRpdGxlID0gJ1RvdGFsSG91cnMgT3V0bGllcnMgUmVtb3ZlZCcpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDQwMDAsIDI1MCkpKSAgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKQ0KDQpgYGANCg0KDQo+IEJ5IHJlbW92aW5nIHRoZSBvdXRsaWVyIHZhbHVlcywgdGhlIHRhaWwgc21hbGwgYnVtcCBhdCA0LDAwMCBob3VycyBpcyBub3RpY2VhYmx5IHJlZHVjZWQuIFRoZSBkaXN0cmlidXRpb24gZm9yICpUb3RhbEhvdXJzKiBsb29rcyB0byBoYXZlIGEgbGFyZ2VyIHZhcmlhbmNlIGNvbXBhcmVkIHRvIHRoYXQgb2YgKkhvdXJzUGVyV2VlayouIEJvdGggZGlzdHJpYnV0aW9ucyBhcmUgc2ltaWxhcmx5IHJpZ2h0LXNrZXdlZCwgd2hpY2ggaXMgdG8gYmUgZXhwZWN0ZWQgc2luY2UgdGhlc2UgdHdvIHZhcmlhYmxlcyBhcmUgbW9zdCBjZXJ0YWlubHkgY29ycmVsYXRlZC4gRGVwZW5kaW5nIG9uIHRoZSBwYXJhbWV0ZXIgc2V0dGluZ3MgZm9yIHRoZSBkZW5zaXR5IHBsb3QgYW5kIGhpc3RvZ3JhbSwgdGhlcmUgbWlnaHQgYmUgZXZpZGVuY2Ugb2YgYmltb2RhbGl0eSwgd2l0aCBhIGRpc3RpbmN0IG1vZGUgb2YgYXBwcm94aW1hdGVseSA0NTAgYW5kIGFub3RoZXIgcG9zc2libGUgb25lIG9mIGF0IGFwcHJveGltYXRlbHkgNzAwLiBUaGUgbWVhbiBvZiA2MzkuMiBjb3JyZXNwb25kcyB0byBhcHByb3hpbWF0ZWx5IDEuNSBob3VycyBvZiBTQzIgcGVyIGRheSwgd2hpY2ggdHJhbnNsYXRlcyB0byBhcm91bmQgZm91ciBhdmVyYWdlIGxlbmd0aCAxIHZzLiAxIG1hdGNoZXMuIA0KDQoNCiMjIypBZ2UqDQoNCg0KPiBBbHRob3VnaCBhZ2UgaXMgb25seSBhbiBpbmRpcmVjdCBpbmRpY2F0b3Igb2YgdGhlIHBhc3NhZ2Ugb2YgdGltZSwgSSB3YW50IHRvIHNlYXJjaCBmb3IgdHJlbmRzIGFjcm9zcyBkaWZmZXJlbnQgYWdlIGdyb3Vwcy4gSW4gdGhlIFN0YXJjcmFmdCBjb21tdW5pdHksIGl0IGlzIGNvbW1vbmx5IGJlbGlldmVkIHRoYXQgb25lJ3Mgc2tpbGwgZGVjbGluZXMgd2l0aCBhZ2UsIHN1Y2ggdGhhdCBwYXN0IHRoZSBlYXJseSB0byBtaWQgMjAncywgaGFuZCBtb3ZlbWVudCBhbmQgcmVhY3Rpb24gc3BlZWRzIGJlZ2luIHRvIHNsb3cgZG93bi4gVGhpcyBiZWxpZWYgaXMgcmVpbmZvcmNlZCBieSB0aGUgZmFjdCB0aGF0IGFuIG92ZXJ3aGVsbWluZyBtYWpvcml0eSBvZiB0b3VybmFtZW50IGNoYW1waW9ucyBhcmUgcmVsYXRpdmVseSB5b3VuZywgb2Z0ZW4gc3RpbGwgaW4gdGhlaXIgdGVlbnMuIFdoaWxlIHRoZSByZXNlYXJjaGVycyBwbGFuIHRvIGludmVzdGlnYXRlIHdoZXRoZXIgb3Igbm90IHN1Y2ggYSBwaHlzaW9sb2dpY2FsIGRlY2xpbmUgYWN0dWFsbHkgb2NjdXJzIGluIHRoZWlyIG5leHQgc3R1ZHksIEkgZmVlbCB0aGF0IGl0IHdvdWxkIGJlIHNlbnNpYmxlIHRvIHNlZSBpZiB0aGVyZSdzIGFueSBzdXBwb3J0aW5nIGV2aWRlbmNlIGZvciB0aGlzIGJlbGllZiB1c2luZyB0aGlzIGRhdGFzZXQsIGV2ZW4gdGhvdWdoIGl0J3Mgbm90IGFzIHNjaWVudGlmaWNhbGx5IHZhbGlkIGFzIGFuYWx5emluZyBhIGxhcmdlIHF1YW50aXR5IG9mIHJlcGxheSBmaWxlcyBhc3NvY2lhdGVkIHdpdGggaW5kaXZpZHVhbCBwbGF5ZXJzJyBleHBlcmllbmNlcyB3aXRoIHRoZSBnYW1lIG92ZXIgYSBsb25nIHBlcmlvZCBvZiB0aW1lLg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiNDb250aW51aW5nIHRoZSBhbmFseXNpcyBieSByZW1vdmluZyBvdXRsaWVyIHZhbHVlcyByYXRoZXIgdGhhbiByZXN0cmljdGluZyB0aGVtDQoNCnNjIDwtIHNjX3JlbW92ZQ0KDQpzdW1tYXJ5KHNjJEFnZSkNCg0KZ2dwbG90KHNjICU+JSBmaWx0ZXIoSG91cnNQZXJXZWVrIDwgNTApLCBtYXBwaW5nID0gYWVzKHggPSBMZWFndWUsIHkgPSBBZ2UpKSArIGdlb21fY29sKGFscGhhPSAwLjMsIGNvbG9yID0gJ3B1cnBsZScpICsgbGFicyh0aXRsZT0nQWdlIHZzLiBMZWFndWUnKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpDQoNCmdncGxvdChzYywgYWVzKHNjJEFnZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gLjUpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDEwLDQ0LCAyKSkpICsgbGFicyh4ID0gJ0FnZSBvZiB0aGUgUGFydGljaXBhbnQnKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpDQoNCg0KZ2dwbG90KHNjLCBhZXMoc2MkQWdlKSkgKyBnZW9tX2RlbnNpdHkoYncgPSAuNSwgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IC41KSArIGxhYnMoeCA9ICdBZ2Ugb2YgdGhlIFBhcnRpY2lwYW50JykgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKHNlcSgxMCw0NCwgMikpKQ0KDQpgYGANCg0KDQo+IEEgbm90aWNlYWJsZSBmZWF0dXJlIG9mIHRoaXMgZGlzdHJpYnV0aW9uIGlzIHRoZSBhYnJ1cHQgY3V0b2ZmIHBvaW50IGF0IDE2LiBUaGlzIGlzIG1vc3QgbGlrZWx5IGR1ZSB0byBldGhpY2FsIHJlc3RyaWN0aW9ucyB0aGF0IHRoZSByZXNlYXJjaGVycyBhZGhlcmVkIHRvIHdpdGggcmVzcGVjdCB0byBhbGxvd2luZyBtaW5vcnMgdG8gcGFydGljaXBhdGUgaW4gdGhlIHN0dWR5LCByYXRoZXIgdGhhbiBhIGxhY2sgb2YgeW91bmdlciBTQzIgcGxheWVycy4gV2VyZSBpdCBub3QgZm9yIHRoaXMsIHRoZSBhZ2UgZGlzdHJpYnV0aW9uIHdvdWxkIGxvb2sgYWxtb3N0IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCB3aXRoIGEgbW9kZSBvZiAyMSBhbmQgbWVhbiBvZiAyMS42NS4gVGhlIGRpc3RyaWJ1dGlvbiBwcm9jZWVkcyBmYWlybHkgc21vb3RobHkgdG8gdGhlIG1heGltdW0gYWdlIG9mIDQ0LiBEdWUgdG8gdGhlIHNtb290aG5lc3Mgb2YgdGhlIGRpc3RyaWJ1dGlvbiwgSSBkZWNpZGVkIHRvIG5vdCB0cmVhdCBwYXJ0aWNpcGFudHMgaW4gdGhlaXIgZWFybHkgNDAncyBhcyBvdXRsaWVycy4gVGhlIHNsb3BlIG9mIHRoZSBkaXN0cmlidXRpb24gc2lnbmlmaWNhbnRseSBkZWNyZWFzZXMgcGFzdCBhZ2UgMjIsIHdoaWNoIGxlbmRzIHN1cHBvcnQgdG8gdGhlIGNvbW11bml0eSdzIHBlcmNlaXZlZCBub3Rpb25zIGNvbmNlcm5pbmcgYWdlIGFuZCBza2lsbCBsZXZlbC4NCg0KDQojIyMqQVBNKiAoQWN0aW9ucyBwZXIgTWludXRlKQ0KDQoNCiFbUGxheWVycyBtdXN0IGJlIGFibGUgdG8gY2xpY2sgb24gdGhlIHJpZ2h0IHBhcnRzIG9mIHRoZSBzY3JlZW4gYXQgdGhlIHJpZ2h0IHRpbWUgd2hpbGUgcmVhY3RpbmcgdG8gd2hhdCB0aGUgb3Bwb25lbnQgaXMgZG9pbmcuXShBUE0uanBnKQ0KDQoNCj4gUHJvY2VlZGluZyB0byB0aGUgaW4tZ2FtZSBtZXRyaWNzLCBJIGZlZWwgdGhhdCB0aGUgbW9zdCBvYnZpb3VzIG9uZSB0byBleHBsb3JlIGlzICpBUE0qLCBzaG9ydCBmb3IgQWN0aW9ucyBQZXIgTWludXRlLiBBbiBhY3Rpb24gaXMgZGVmaW5lZCB0byBiZSBhbnkga2V5IHByZXNzIG9yIG1vdXNlIGNsaWNrIHBlcmZvcm1lZCBieSB0aGUgcGxheWVyLiBJbiBTQzIsICpBUE0qIGlzIGFsbW9zdCBzeW5vbnltb3VzIHdpdGggbGV2ZWwgc2tpbGwuIEl0IGlzIHR5cGljYWxseSB1bmRlcnN0b29kIHRoYXQgaWYgYSBwbGF5ZXIgY2FuJ3QgcGVyZm9ybSBhY3Rpb25zIGF0IGEgcmF0ZSB0aGF0IGlzIGNvbXBhcmFibGUgdG8gdGhhdCBvZiB0aGUgb3Bwb25lbnQsIHRoZW4gaGUgb3Igc2hlIHNpbXBseSBjYW4ndCBrZWVwIHVwIGFuZCB3aWxsIGV2ZW50dWFsbHkgYmUgb3ZlcndoZWxtZWQgZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlIG9wcG9uZW50IGNhbiBhY2NvbXBsaXNoIG1vcmUgaW4gdGhlIHNhbWUgYW1vdW50IG9mIHRpbWUuIFRoZSBkZXNjcmlwdGlvbiBhY2NvbXBhbnlpbmcgdGhpcyBkYXRhc2V0IGRvZXMgbm90IHNwZWNpZnkgd2hldGhlciB0aGUgaW5jbHVkZWQgKkFQTSogdmFsdWVzIGFyZSBhdmVyYWdlIG9yIG1heGltdW0gdmFsdWVzIGFjaGlldmVkIGR1cmluZyBlYWNoIGdhbWUuIEkgYXNzdW1lIHRoYXQgdGhleSByZXByZXNlbnQgYXZlcmFnZSB2YWx1ZXMgc2luY2UgYXZlcmFnZSBBUE0gaXMgb25lIG9mIHRoZSBtZXRyaWNzIHRoYXQgdGhlIGdhbWUgcHJlc2VudHMgdG8gdGhlIHBsYXllciBhdCB0aGUgY29uY2x1c2lvbiBvZiBlYWNoIGdhbWUuDQoNCg0KPiBEdXJpbmcgYmF0dGxlcywgYSBza2lsbGVkIHBsYXllciBzaG91bGQgYmUgYWJsZSB0byBwb3NpdGlvbiBoaXMgYXJteSBwcm9wZXJseSBhbmQgbWljcm9tYW5hZ2Ugc2V2ZXJhbCB1bml0cyBzaW11bHRhbmVvdXNseSB3aXRob3V0IG5lZ2xlY3RpbmcgdGhlIG1vcmUgbXVuZGFuZSB0YXNrcyBvZiBwcm9kdWNpbmcgd29ya2VycyBhbmQgY29uc3RydWN0aW5nIGJ1aWxkaW5ncyBiYWNrIGF0IGJhc2UuIFdpbm5pbmcgYSBiYXR0bGUgd2hpbGUgZm9yZ2V0dGluZyB0byBtYW5hZ2Ugb25lJ3MgYmFzZXMgY291bGQgbGVhdmUgb25lIGluIGEgd29yc2UgcG9zaXRpb24gYW5kIGFsbG93IHRoZSBvcHBvbmVudCB0byBjYXRjaCB1cCBpbiB0aGUgbmVhciBmdXR1cmUuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCnN1bW1hcnkoc2MkQVBNKQ0KDQpnZ3Bsb3Qoc2MsIGFlcyhzYyRBUE0pKSArIGdlb21fZGVuc2l0eShidyA9IDMsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAuNSkgKyBsYWJzKHggPSAnQWN0aW9ucyBQZXIgTWludXRlJykgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKHNlcSgyMCw0MDAsIDIwKSkpDQoNCmBgYA0KDQoNCj4gVGhlIGRpc3RyaWJ1dGlvbiBmb3IgKkFQTSogbG9va3MgdG8gYmUgYXBwcm94aW1hdGVseSBub3JtYWxseSBkaXN0cmlidXRlZCwgd2l0aCBhIG1lZGlhbiBvZiAxMDggYW5kIGEgbWVhbiBvZiAxMTcuIFRoZSBzbG9wZSAgb24gdGhlIGxlZnQgc2lkZSBpcyBtdWNoIHN0ZWVwZXIsIHdpdGggYSBtaW5pbXVtIHZhbHVlIG9mIDIwLiBUaGlzIHNvdW5kcyByZWFzb25hYmxlLCBzaW5jZSAyMCBhY3Rpb25zIHBlciBtaW51dGUgY2FuIGJlIGFjaGlldmVkIGJ5IG5lYXJseSBhbnkgYWJsZS1ib2RpZWQgcGVyc29uIHVzaW5nIGEgY29tcHV0ZXIgd2l0aG91dCBtdWNoIGVmZm9ydC4gT24gdGhlIG90aGVyIGVuZCwgd2Ugc2VlIHNvbWUgdmFsdWVzIGV4Y2VlZGluZyAzNjAsIHdoaWNoIHRyYW5zbGF0ZXMgdG8gYSBzdXN0YWluZWQgcmF0ZSBvZiA2IGFjdGlvbnMgcGVyIHNlY29uZC4gVGhvdWdoIHRoZXJlIGhhdmUgYmVlbiBhIGZldyBwcm9mZXNzaW9uYWwgcGxheWVycyB3aG8gd2VyZSBrbm93biBmb3IgY29uc2lzdGVudGx5IGF0dGFpbmluZyBzdWNoIGV4Y2VwdGlvbmFsbHkgaGlnaCBBUE0gdmFsdWVzLCBpdCBjb3VsZCBhbHNvIGJlIHRoZSBjYXNlIHRoYXQgdGhlICpBUE0qIG91dGxpZXIgdmFsdWVzIGluIHRoaXMgZGF0YXNldCBhcmUgZHVlIHRvIHNvbWUgcGxheWVycyBpbmZsYXRpbmcgdGhlaXIgQVBNIGJ5IHJhcGlkbHkgcGVyZm9ybWluZyBtZWFuaW5nbGVzcyBhY3Rpb25zLCBzdWNoIGFzIGNsaWNraW5nIGVtcHR5IHNwYWNlcy4gT2Z0ZW4sIHBsYXllcnMgZG8gdGhpcyBhdCB0aGUgYmVnaW5uaW5nIG9mIGEgZ2FtZSB0byB3YXJtIHVwIHRoZWlyIGhhbmQgbXVzY2xlcyBzaW5jZSB0aGVyZSdzIHR5cGljYWxseSBub3QgbXVjaCB0byBkbyBhdCBzdWNoIGFuIGVhcmx5IHN0YWdlIG9mIGEgZ2FtZS4gSG93ZXZlciwgaXQgY291bGQgYmUgdGhlIGNhc2UgdGhhdCBzb21lIHBsYXllcnMgY29udGludWUgZG9pbmcgdGhpcyB0aHJvdWdob3V0IHRoZWlyIGdhbWVzLCBwb3NzaWJseSB3aXRoIHRoZSBpbnRlbnRpb24gb2YgaW5mbGF0aW5nIHRoZWlyIEFQTSByYXRlLiBBIGZldyB5ZWFycyBhZnRlciB0aGVzZSByZXBsYXlzIHdlcmUgY29sbGVjdGVkLCBhbiBpbXByb3ZlZCBBUE0gbWVhc3VyZW1lbnQgdGhhdCBmaWx0ZXJzIG91dCBzdWNoIG1lYW5pbmdsZXNzIGFjdGlvbnMgd2FzIGFkZGVkIHRvIHRoZSBnYW1lLiBVc2luZyB0aGF0IG1ldHJpYywgd2Ugd291bGQgYmUgdW5saWtlbHkgdG8gc2VlIHN1Y2ggZXh0cmVtZSB2YWx1ZXMuDQoNCg0KYGBge3Igd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRBUE0pKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnQWN0aW9ucyBwZXIgTWludXRlJykgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgNDAwLCAyNSkpKQ0KDQpgYGANCg0KDQo+IFBsb3R0aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgQVBNIHZlcnN1cyBsZWFndWUgcmFuayBzdXBwb3J0cyB0aGUgaWRlYSB0aGVzZSBvdXRsaWVyICpBUE0qIHZhbHVlcyB3ZXJlIGFydGlmaWNpYWxseSBpbmZsYXRlZC4gVGhleSBvcmlnaW5hdGUgZnJvbSBwbGF5ZXJzIGluIERpYW1vbmQgYW5kIE1hc3RlciBMZWFndWUsIHdoaWNoIGlzIGZhciBmcm9tIHRoZSBoaWdoZXN0IGxldmVscyBvZiBjb21wZXRpdGl2ZSBwbGF5LiBUaGVzZSBvdXRsaWVycyBhcmUgZmFyIHJlbW92ZWQgZnJvbSB0aGUgbWVkaWFuIHZhbHVlcyBvZiB0aGVpciByZXNwZWN0aXZlIGxlYWd1ZXMuIFRoZSBmYWN0IHRoYXQgc2V2ZXJhbCB2YWx1ZXMgZXhjZWVkIGV2ZW4gdGhlIGhpZ2hlc3QgQVBNIGZvciBHcmFuZE1hc3RlciBwbGF5ZXJzIHN1Z2dlc3RzIHRoYXQgdGhleSBhcmUgbm90IHJlYWxpc3RpY2FsbHkgYXR0YWluYWJsZSB2YWx1ZXMgZm9yIHBsYXllcnMgaW4gdGhlc2UgbGVhZ3Vlcy4gVGhlcmVmb3JlLCBJIGRlY2lkZWQgdG8gcmVtb3ZlIGRhdGEgcG9pbnRzIHdoZXJlICpBUE0qIHZhbHVlcyBleGNlZWQgMjc1LCB3aGljaCBpcyB0aGUgYXBwcm94aW1hdGUgbWVkaWFuIHZhbHVlIGZvciB0aGUgcHJvZmVzc2lvbmFsIHBsYXllcnMgcmVwcmVzZW50ZWQgaW4gdGhpcyBkYXRhc2V0Lg0KDQoNCmBgYHtyIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQoNCiNSZW1vdmUgZGF0YSBwb2ludHMgYmVsb25naW5nIHRvIERpYW1vbmQgYW5kIE1hc3RlciBMZWFndWUgdGhhdCBoYXZlIG92ZXIgMjc1IEFQTQ0KDQpzYyA8LSBzY1shKHNjJEFQTSA+IDI3NSAmIHNjJExlYWd1ZSA9PSAnRGlhbW9uZCcpLF0NCnNjIDwtIHNjWyEoc2MkQVBNID4gMjc1ICYgc2MkTGVhZ3VlID09ICdNYXN0ZXInKSxdDQoNCmdncGxvdChzYywgYWVzKHNjJEFQTSkpICsgZ2VvbV9kZW5zaXR5KGJ3ID0gMywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IC41KSArIGxhYnMoeCA9ICdBY3Rpb25zIFBlciBNaW51dGUnKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDIwLDQwMCwgMjApKSkNCg0KYGBgDQoNCg0KDQpgYGB7cn0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRBUE0pKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS41LCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ0FjdGlvbnMgUGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDQwMCwgMjUpKSkNCg0KYGBgDQoNCg0KDQo+IEhhdmluZyByZW1vdmVkIHRoZXNlIG91dGxpZXIgdmFsdWVzLCB0aGUgc2hhcGUgb2YgdGhlIGRlbnNpdHkgcGxvdCBkaWQgbm90IGNoYW5nZSBtdWNoLCBidXQgdGhlIGJveHBsb3QgbG9va3MgbW9yZSByZWFzb25hYmxlLiBUaGVyZSBpcyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gKkxlYWd1ZSogYW5kICpBUE0qIHRoYXQgc3VwcG9ydHMgdGhlIGlkZWEgdGhhdCBBUE0gaXMgYSByZWFzb25hYmxlIGluZGljYXRvciBvZiBza2lsbCBsZXZlbC4gSG93ZXZlciwgaXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgcGxheWVyIGFjdGlvbnMgY2FuIHJhcmVseSBiZSBhY2N1cmF0ZWx5IGNhdGVnb3JpemVkIGFzIGVpdGhlciAibWVhbmluZ2Z1bCIgb3IgIm1lYW5pbmdsZXNzIi4gVGhlcmUgYXJlIG9mdGVuIHNldmVyYWwgZXZlbnRzIGhhcHBlbmluZyBzaW11bHRhbmVvdXNseSBhY3Jvc3MgdGhlIGJhdHRsZWZpZWxkIHRoYXQgZGVtYW5kIGEgcGxheWVyJ3MgYXR0ZW50aW9uLiBTQzIgZXhwZXJ0aXNlIGhlYXZpbHkgZGVwZW5kcyBvbiB0aGUgYWJpbGl0eSB0byBwcmlvcml0aXplIHRoZXNlIGV2ZW50cyBhbmQgZm9jdXMgYXR0ZW50aW9uIG9uIHRoZSBtb3N0IGltcG9ydGFudCBvbmVzLiBUbyBnaXZlIHR3byBleHRyZW1lIGV4YW1wbGVzLCBhIHBsYXllciB3aG8gZGV2b3RlIGZ1bGwgYXR0ZW50aW9uIHRvIG9ubHkgY29udHJvbGxpbmcgYSBzaW5nbGUgdW5pdCBhdCBhIHRpbWUgd2lsbCBtb3N0IGxpa2VseSBub3Qgd2luIHRvbyBtYW55IGdhbWVzLCBtdWNoIGxpa2UgYSBwbGF5ZXIgd2hvIGluc3RhbnRseSBzaGlmdHMgaGlzIG9yIGhlciBhdHRlbnRpb24gdG8gdGhlIGxhdGVzdCBldmVudCB3aXRob3V0IGFueSBwcmlvcml0aXphdGlvbi4gRXZlbiB3aXRoICJtZWFuaW5nZnVsIiBhY3Rpb25zLCB0aGUgZGVjaXNpb25zIHRoYXQgYSBwbGF5ZXIgbWFrZXMgY2FuIGJlIGp1ZGdlZCBvbiBhIGNvbnRpbnVvdXMgc2NhbGUgb2YgZWZmZWN0aXZlbmVzcyB0aGF0IGRlcGVuZHMgb24gdGhlIGNvbnRleHQgb2Ygd2hhdCBpcyBoYXBwZW5pbmcgYW5kIHdoYXQgdGhlIHBsYXllciBrbm93cyBhdCB0aGF0IG1vbWVudC4gSW4gYnJvYWRjYXN0ZWQgZ2FtZXMsIGNvbW1lbnRhdG9ycyBhcmUgZXhwZWN0ZWQgdG8gYW5hbHl6ZSB0aGVzZSBkZWNpc2lvbnMgYW5kIHBvaW50IG91dCB3aGljaCBvbmVzIHRoZXkgdGhpbmsgYXJlIHdpc2Ugb3IgdW53aXNlLiBUaHVzLCBvbmUgY2Fubm90IGRlY2xhcmUgdGhhdCBvbmUgcGxheWVyIGlzIGRlZmluaXRlbHkgYmV0dGVyIHRoYW4gYW5vdGhlciBwbGF5ZXIgYmFzZWQgc29sZWx5IG9uIGEgY29tcGFyaXNvbiBvZiBBUE0uDQoNCg0KIyM0LiBFeGVjdXRpdmUgU3VtbWFyeQ0KDQoNCj4gDQoNCg0KIyM1LiBNYWluIEFuYWx5c2lzDQoNCg0KPiBJdCBpcyBjb21tb25seSB0aG91Z2h0IHRoYXQgaGlnaGVyIEFQTSBjb21lcyBuYXR1cmFsbHkgdGhyb3VnaCBiZWNvbWluZyBtb3JlIHNraWxsZWQgYXQgU0MyLiBJbiB0aGUgU0MyIGNvbW11bml0eSwgaXQgaXMgYmVsaWV2ZWQgdGhhdCB0aGVyZSBpcyBhIHJvdWdoIGhlaXJhcmNoeSBvZiBza2lsbHMgdGhhdCBvbmUgc2hvdWxkIGFjcXVpcmUgaW4gb3JkZXIgdG8gYmVjb21lIHNraWxsZWQuIFRoZSB2YXJpYWJsZXMgY29ycmVzcG9uZGluZyB0byB0aGVzZSBza2lsbHMsIHJhbmtlZCBpbiBhcHByb3hpbWF0ZSBkZXNjZW5kaW5nIG9yZGVyIG9mIGltcG9ydGFuY2UsIGFyZSB0aGUgZm9sbG93aW5nOg0KDQoNCjEuICpXb3JrZXJzTWFkZSoNCjIuICpNaW5pbWFwQXR0YWNrcyogLyAqTWluaW1hcFJpZ2h0Q2xpY2tzKg0KMy4gKkFzc2lnblRvSG90a2V5cyogLyAqU2VsZWN0QnlIb3RrZXlzKg0KNC4gKlRvdGFsTWFwRXhwbG9yZWQqDQo1LiAqVW5pcXVlVW5pdHNNYWRlKg0KNi4gKkNvbXBsZXhBYmlsaXRpZXNVc2VkKiAvICpDb21wbGV4VW5pdHNNYWRlKg0KNy4gKlVuaXF1ZUhvdGtleXMqDQoNCg0KPiBJdCBzaG91bGQgYmUgbm90ZWQgdGhhdCB0aGVzZSB2YXJpYWJsZXMgYXJlIHJhdGVzIGV4cHJlc3NlZCBpbiAidGltZXN0YW1wcyIsIHdoaWNoIGlzIHRoZSB3YXkgdGhhdCB0aGUgZ2FtZSBjYWxjdWxhdGVzIHRpbWUuIFRoZXJlIGFyZSBhcHByb3hpbWF0ZWx5IDg4LjUgdGltZXN0YW1wcyBwZXIgc2Vjb25kIGFjY29yZGluZyB0byB0aGUgZGF0YXNldCBkb2N1bWVudGF0aW9uLiBTaW5jZSBzdWNoIGEgc2hvcnQgbWVhc3VyZSBvZiB0aW1lIGNyZWF0ZXMgdmVyeSBzbWFsbCB2YWx1ZXMsIEkgd2lsbCBleHByZXNzIHRoZXNlIHZhcmlhYmxlcyBpbiBtaW51dGVzIHRvIG1ha2UgdGhlIGRhdGEgYW5kIGdyYXBocyBlYXNpZXIgdG8gdW5kZXJzdGFuZC4gVGhyb3VnaG91dCB0aGlzIHNlY3Rpb24sIEkgZW5jb3VudGVyZWQgb3V0bGllcnMgZm9yIHNldmVyYWwgdmFyaWFibGVzIHRoYXQgbWFkZSBpdCBkaWZmaWN1bHQgdG8gcmVhZCB0aGUgZ3JhcGhzLiBJIGRlYWx0IHdpdGggdGhlc2Ugb3V0bGllciB2YWx1ZXMgYnkgcmVtb3ZpbmcgdGhlbSBmcm9tIHRoZSBkYXRhc2V0LiBJIG1hZGUgdGhpcyBkZWNpc2lvbiBkdWUgdG8gdGhlc2UgZGF0YSBwb2ludHMgY29tcHJpc2luZyBhbiBleHRyZW1lbHkgc21hbGwgcG9ydGlvbiBvZiB0aGUgZGF0YXNldC4gQWxzbywgc2luY2UgbW9zdCBvZiB0aGVzZSBtZXRyaWNzIGFyZSBtb3JlIG9ic2N1cmUgYW5kIG5vdCBkaXNjdXNzZWQgb2Z0ZW4gaW4gdGhlIGNvbW11bml0eSwgaXQgd291bGQgYmUgZGlmZmljdWx0IHRvIGRlY2lkZSBvbiBhICJyZWFzb25hYmxlIiBjZWlsaW5nIHZhbHVlIHRvIHJlc3RyaWN0IHRoZXNlIHZhcmlhYmxlcyB0by4NCg0KDQo+IEluIGFkZGl0aW9uLCBJIGRlY2lkZWQgdG8gb21pdCB0aGUgKk1heFRpbWVTdGFtcCogdmFyaWFibGUgZnJvbSBteSBhbmFseXNpcy4gVGhpcyB2YXJpYWJsZSBpbmRpY2F0ZXMgdGhlIGxlbmd0aCBvZiBlYWNoIGdhbWUuIFdoaWxlIHRoZXJlIG1heSBiZSBzb21lIGNvcnJlbGF0aW9uIGJldHdlZW4gaG93IHNraWxsZWQgYSBwbGF5ZXIgaXMgYW5kIGhpcyBvciBoZXIgYXZlcmFnZSBnYW1lIGxlbmd0aCwgZWFjaCBwbGF5ZXIncyBjaG9pY2UgaW4gc3RyYXRlZ3kgaGFzIGEgbWFqb3IgZWZmZWN0IG9uIGhvdyBsb25nIGEgcGFydGljdWxhciBnYW1lIHdpbGwgYmUuIEZvciBleGFtcGxlLCBiZWZvcmUgYSBnYW1lIGJlZ2lucywgYSBwbGF5ZXIgY2FuIGRlY2lkZSB0byBpbnZlc3Qgc2lnbmlmaWNhbnQgcmVzb3VyZXMgZWFybHkgb24gaW50byBkb2luZyBhIHF1aWNrIHN1cnByaXNlIGF0dGFjayB0byBjYXRjaCB0aGUgb3Bwb25lbnQgb2ZmIGd1YXJkLiBJZiBzdWNoIGFuIGF0dGFjayBmYWlscywgdGhlbiB0aGUgYXR0YWNraW5nIHBsYXllciB3aWxsIGJlIGluIGEgdnVsbmVyYWJsZSBwb3NpdGlvbiBhbmQgbWF5IGJlIGRlZmVhdGVkIGJ5IHRoZSBvcHBvbmVudCdzIGNvdW50ZXJhdHRhY2suIEluIHRoaXMgY2FzZSwgdGhlIGdhbWUgaXMgbGlrZWx5IHRvIGVuZCBlYXJseSByZWdhcmRsZXNzIG9mIHRoZSBvdXRjb21lLiBPbiB0aGUgb3RoZXIgaGFuZCwgYm90aCBwbGF5ZXJzIGNhbiBqdXN0IGFzIGVhc2lseSBkZWNpZGUgdG8gZm9jdXMgb24gdGhlaXIgb3duIGFybXkgYW5kIGJ1aWxkaW5nIHByb2R1Y3Rpb24gd2hpbGUgYXZvaWRpbmcgY29uZmxpY3RzIGVhcmx5IG9uLiBTdWNoIGEgZ2FtZSB3b3VsZCB0YWtlIG11Y2ggbG9uZ2VyIHRvIGZpbmlzaC4gU2luY2UgZ2FtZSBsZW5ndGggaXMgbGFyZ2VseSBhZmZlY3RlZCBieSBjb25zY2lvdXMgY2hvaWNlcyB0aGF0IHBsYXllcnMgbWFrZSBpbiBhZGRpdGlvbiB0byBiZWhhdmlvciBkZXJpdmVkIGZyb20gc2tpbGwgbGV2ZWwsIGl0IHdvdWxkIGJlIGRpZmZpY3VsdCB0byBpc29sYXRlIHRoZXNlIHR3byBmYWN0b3JzIGluIG9yZGUgdG8gYXNzZXNzICpNYXhUaW1lU3RhbXAqIGFzIGFuIGluZGljYXRvciBvZiBza2lsbCBsZXZlbC4NCg0KDQojIyMxKSAqV29ya2Vyc01hZGUqDQoNCg0KPiBUaGUgdHlwaWNhbCBhbnN3ZXIgdG8gdGhlIHF1ZXN0aW9uIG9mIGhvdyBvbmUgZ2V0cyBiZXR0ZXIgYXQgU0MyIGlzIHRvIG1ha2UgbW9yZSB3b3JrZXJzLiBUaGlzIG1pZ2h0IHNlZW0gc3VycHJpc2luZ2x5IGF0IGZpcnN0LiBXb3JrZXIgdW5pdHMgYXJlIHByaW1hcmlseSByZXNwb25zaWJsZSBmb3IgY29sbGVjdGluZyByZXNvdXJjZXMgYW5kIGNvbnN0cnVjdGluZyBidWlsZGluZ3MuIFRoaXMgbWlnaHQgc291bmQgcXVpdGUgbXVuZGF0ZSwgYnV0IGNvbGxlY3RpbmcgZW5vdWdoIHJlc291cmNlcyBpcyBvZiB1dG1vc3QgaW1wb3J0YW5jZSwgc2luY2UgdGhpcyBpcyB0aGUgY3VycmVuY3kgd2l0aCB3aGljaCBwbGF5ZXJzIGJ1aWxkIHRoZWlyIGFybXkgYW5kIGJhc2VzLiBDb2xsZWN0aW5nIHJlc291cmNlcyBhdCBhIHN1Ym9wdGltYWwgcmF0ZSBkdWUgdG8gaGF2aW5nIHRvbyBmZXcgd29ya2VycyB3aWxsIGNyZWF0ZSBhIGJvdHRsZW5lY2sgdGhhdCB3aWxsIHNsb3cgdGhlIHJhdGUgYXQgd2hpY2ggeW91ciBhcm15IGNhbiBiZSBwcm9kdWNlZC4gQWRkaXRpb25hbGx5LCB3b3JrZXJzIGNhbiBiZSBzZXJ2ZSBhcyBzY291dHMgb3Igc2FjcmlmaWNpYWwgcGF3bnMgZHVyaW5nIGF0dGFja3MgYW5kIGRlZmVuc2VzLiBQZW9wbGUgb2Z0ZW4gdGVsbCBub3ZpY2VzIHRoYXQgdGhleSBjYW4gZ2V0IHByb21vdGVkIG91dCBvZiBCcm9uemUgTGVhZ3VlIHdpdGggdGhpcyBzaW1wbGUgc3RyYXRlZ3k6Y29uc3RhbnRseSBwcm9kdWNlIHdvcmtlcnMsIGNvbGxlY3QgbG90cyBvZiByZXNvdXJjZXMsIGJ1aWxkIGFuIGFybXkgd2l0aCB0aG9zZSByZXNvdXJjZXMgKHRoZSB0eXBlIG9mIHVuaXRzIGJ1aWx0IGRvZXNuJ3QgcmVhbGx5IG1hdHRlciksIGFuZCBzZW5kIHRoYXQgYXJteSBzdHJhaWdodCB0byB0aGUgZW5lbXkgYmFzZS4NCg0KDQohW1RoZXNlIGh1bWJsZSB3b3JrZXJzIGFyZSBidXN5IGNvbGxlY3RpbmcgcmVzb3VyY2VzIGluc2lkZSB0aGlzIHBsYXllcidzIG1haW4gYmFzZS5dKHdvcmtlcnMuanBnKQ0KDQoNCiFbVGhpcyBncmFwaCBzaG93aW5nIHRoZSBudW1iZXIgb2Ygd29ya2VycyBlYWNoIHBsYXllciBwb3NzZXNzZXMgYXQgYW55IGdpdmVuIHRpbWUgaXMganVzdCBvbmUgb2Ygc2V2ZXJhbCBwYWdlcyBvZiBkZXRhaWxlZCBzdGF0aXN0aWNzIHRoYXQgYXJlIHByZXNlbnRlZCB0byBwbGF5ZXJzIGF0IHRoZSBjb25jbHVzaW9uIG9mIGVhY2ggZ2FtZS5dKGdyYXBoLmpwZykNCg0KDQo+IFRoZSB2aW9saW4gcGxvdCBiZWxvdyBzaG93cyB0aGF0IHRoZXJlIGlzIGEgc2xpZ2h0bHkgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiBob3cgd29ya2VycyBhcmUgcHJvZHVjZWQgcGVyIG1pbnV0ZSBhbmQgdGhlIHJhbmsgb2YgdGhlIHBsYXllci4gQnkgbG9va2luZyBhdCB0aGUgbWVkaWFuIGxpbmVzLCBvbmUgY2FuIHNlZSBhIHNsaWdodCBkb3dud2FyZCB0cmVuZCBnb2luZyBmcm9tIE1hc3RlciB0byBHcmFuZE1hc3Rlci4gQSBtb3JlIHByb25vdW5jZWQgaW5jcmVhc2UgY2FuIGJlIHNlZW4gd2hlbiBsb29raW5nIGF0IHRoZSBvdXRsaWVyIHZhbHVlcyBmcm9tIERpYW1vbmQgdG8gR3JhbmRNYXN0ZXIuIFRoaXMgZGVtb25zdHJhdGVzIHRoYXQgb25lIHNob3VsZCBmaW5kIHRoZSBwcm9wZXIgYmFsYW5jZSBiZXR3ZWVuIG1ha2luZyB0b28gZmV3IHdvcmtlcnMgKGNvbGxlY3RpbmcgcmVzb3VyY2VzIGF0IGEgc3Vib3B0aW1hbCByYXRlKSBhbmQgbWFraW5nIHRvbyBtYW55ICh3YXN0aW5nIG1vbmV5IGJ5IHByb2R1Y2luZyB1bm5lZWRlZCB3b3JrZXJzIHdobyBoYXZlIG5vdGhpbmcgdG8gZG8pLiBPbiB0aGUgb3RoZXIgZW5kLCB0aGUgdHJhbnNpdGlvbiBmcm9tIEJyb256ZSB0byBTaWx2ZXIgcmVzdWx0cyBpbiB0aGUgbWVkaWFuIGluY3JlYXNpbmcgZnJvbSAyLjkzNCB0byAzLjg0LCB3aGljaCBpcyBtb3JlIHRoYW4gdGhlIHRyYW5zaXRpb24gYmV0d2VlbiBhbnkgb3RoZXIgcGFpcnMgb2YgYWRqYWNlbnQgbGVhZ3Vlcy4gVGhpcyBzdXBwb3J0cyB0aGUgY2xhaW0gdGhhdCBwbGF5ZXJzIGNhbiBiZSBwcm9tb3RlZCBvdXQgb2YgQnJvbnplIExlYWd1ZSBzaW1wbHkgYnkgZm9jdXNpbmcgb24gaW1wcm92aW5nIHRoZWlyIHdvcmtlciBwcm9kdWN0aW9uIGhhYml0cy4gTWVhbndoaWxlLCB3b3JrZXIgcHJvZHVjdGlvbiByYXRlcyBsZXZlbCBvdXQgYXQgdGhlIGhpZ2hlciBsZWFndWVzLiBUaGUgb3V0bGllciB2YWx1ZXMgaW4gRGlhbW9uZCBsZWFndWUgY291bGQgYmUgYSBzaWduIG9mIHBsYXllcnMgcHJvZHVjaW5nIG1vcmUgd29ya2VycyB0aGFuIG5lZWRlZCwgd2hpY2ggd291bGQgYmUgYW4gaW5lZmZpY2llbnQgdXNlIG9mIHJlc291cmNlcy4NCg0KDQpgYGB7ciB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQ0KDQpnZ3Bsb3Qoc2MsIGFlcyhzYyRMZWFndWUsIHNjJFdvcmtlcnNNYWRlKjg4LjUqNjApKSArIGdlb21fdmlvbGluKGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUsIGRyYXdfcXVhbnRpbGVzID0gYygwLjUpKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS41LCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ1dvcmtlcnMgUHJvZHVjZWQgcGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsMzAsIDIpKSkNCg0KI0Rpc3BsYXkgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciAiV29ya2Vyc01hZGUiIGZvciBlYWNoIGxlYWd1ZQ0KDQpmb3IobGVhZ3VlIGluIHJhbmspIHsNCiAgcHJpbnQobGVhZ3VlKQ0KICBwcmludChzdW1tYXJ5KGZpbHRlcihzYywgc2MkTGVhZ3VlID09IGxlYWd1ZSkkV29ya2Vyc01hZGUqODguNSo2MCkpDQp9DQoNCmBgYA0KDQoNCiMjIzIpICpNaW5pbWFwQXR0YWNrcyogLyAqTWluaW1hcFJpZ2h0Q2xpY2tzKg0KDQoNCj4gVGhlIHNlY29uZCBtb3N0IGltcG9ydGFudCBtZXRyaWMgbWlnaHQgYmUgYSBzdXJwcmlzaW5nIGNob2ljZSBhcyB3ZWxsLiBUaGUgbWluaW1hcCBpcyBhIG1hcCBvZiB0aGUgYmF0dGxlZmllbGQgbG9jYXRlZCBvbiB0aGUgYm90dG9tIGxlZnQgY29ybmVyIG9mIHRoZSB1c2VyIGludGVyZmFjZSAoYXMgc2hvd24gaW4gdGhlIGZpcnN0IGdhbWVwbGF5IHNjcmVlbnNob3QgaW4gdGhlIGludHJvZHVjdGlvbiBzZWN0aW9uKS4gQXQgYSBnbGFuY2UsIHBsYXllcnMgY2FuIHNlZSB3aGF0IGlzIGhhcHBlbmluZyB3aGVyZXZlciBoZSBoYXMgYW4gYXJteSBwcmVzZW5jZS4gQnkgY2xpY2tpbmcgb24gdGhlIG1pbmltYXAsIHBsYXllcnMgY2FuIGluc3RhbnRseSBzd2l0Y2ggdmlld3MgdG8gdGhlIGNvcnJlc3BvbmRpbmcgbG9jYXRpb24uIE1vcmUgaW1wb3J0YW50bHksIGhlIG9yIHNoZSBjYW4gb3JkZXIgdW5pdHMgdG8gbW92ZSB0byBvciBhdHRhY2sgdGhhdCBsb2NhdGlvbi4gVGhlIGFsdGVybmF0aXZlIGlzIGZvciB0aGUgcGxheWVyIHRvIG1hbnVhbGx5IHNjcm9sbCB0byB0aGF0IGxvY2F0aW9uIHdpdGggdGhlIG1vdXNlLHdoaWNoIGNvdWxkIHRha2UgbXVjaCBsb25nZXIgZGVwZW5kaW5nIG9uIGhvdyBiaWcgdGhlIGJhdHRsZWZpZWxkIGlzIGFuZCB3aGVyZSB0aGUgcGxheXRlciB3YXMgbG9va2luZyBwcmlvciB0byBzY3JvbGxpbmcuIFdpdGggdGhlIG1pbmltYXAsIHBsYXllcnMgY2FuIG9ic2VydmUgYW5kIHJlYWN0IHRvIHNpdHVhdGlvbnMgb2NjdXJyaW5nIGluIHRoZSBnYW1lIGZhc3RlciBhbmQgbW9yZSBlZmZpY2llbnRseS4gVGhpcyBkaWZmZXJlbmNlIGNhbiBiZSBjb21wYXJlZCB0byB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGVkaXRpbmcgYSBsb25nIGRvY3VtZW50IHdpdGggYWJpbGl0eSB0byBqdW1wIHRvIGRpZmZlcmVudCBzZWN0aW9ucyBieSBjbGlja2luZyBvciBzY3JvbGxpbmcgd2l0aCBhIG1vdXNlIHZlcnN1cyBoYXZpbmcgdG8gbWFudWFsbHkgc2Nyb2xsIHRocm91Z2ggdGhlIHRleHQgdXNpbmcgdGhlIGFycm93IGtleXMgb24gdGhlIGtleWJvYXJkLg0KDQoNCiFbVGhpcyBpcyB0aGUgaW50ZXJmYWNlIHRocm91Z2ggd2hpY2ggcGxheWVycyBpbnRlcmFjdCB3aXRoIHRoZWlyIGJhc2VzIGFuZCBhcm1pZXMgaW4gU3RhcmNyYWZ0IElJLiBUaGUgbWluaW1hcCBpcyBwb3NpdGlvbmVkIGF0IHRoZSBib3R0b20gbGVmdCBjb3JuZXIuXShtaW5pbWFwLmpwZykNCg0KDQpgYGB7cn0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRNaW5pbWFwQXR0YWNrcyo4OC41KjYwKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvciA9ICdibHVlJywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuNSkgKyAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdNaW5pbWFwIEF0dGFja3MgSXNzdWVkIHBlciBNaW51dGUnKQ0KDQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkTWluaW1hcFJpZ2h0Q2xpY2tzKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnTWluaW1hcCBSaWdodCBDbGlja3MgUGVyZm9ybWVkIHBlciBNaW51dGUnKQ0KDQoNCmBgYA0KDQoNCj4gVGhlcmUgYXJlIGEgZmV3IG91dGxpZXIgZGF0YSBwb2ludHMgdGhhdCBhcmUgb2JzY3VyaW5nIHRoZSB2aWV3IG9mIHRoZSBsZWFndWUgZGlzdHJpYnV0aW9ucyBmb3IgKk1pbmltYXBBdHRhY2tzKiAoMTcvMzM5NSkgYW5kICpNaW5pbWFwUmlnaHRDbGlja3MqICg3LzMzOTUpLiBBZnRlciByZW1vdmluZyB0aGVzZSBwb2ludHMsIHRoZSBkaXN0cmlidXRpb25zIGNhbiBiZSBtb3JlIGNsZWFybHkgc2VlbjoNCg0KDQpgYGB7cn0NCg0KI1JlbW92ZSB0aGUgIk1pbmltYXBBdHRhY2tzIiBvdXRsaWVyIHZhbHVlcyBhbmQgY29udmVydCB0aGUgdmFsdWVzIGluIHRlcm1zIG9mIG1pbnV0ZXMNCg0Kc2MgPC0gZmlsdGVyKHNjLCBzYyRNaW5pbWFwQXR0YWNrcyA8PSAwLjAwMTAwMykNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRNaW5pbWFwQXR0YWNrcyo4OC41KjYwKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvciA9ICdibHVlJywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuNSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG9lc3MnLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuNSwgYWVzKGdyb3VwID0gMSkpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdNaW5pbWFwIEF0dGFja3MgSXNzdWVkIHBlciBNaW51dGUnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDcsIDAuMjUpKSkNCg0KZm9yKGxlYWd1ZSBpbiByYW5rKSB7DQogIHByaW50KGxlYWd1ZSkNCiAgcHJpbnQoc3VtbWFyeShmaWx0ZXIoc2MsIHNjJExlYWd1ZSA9PSBsZWFndWUpJE1pbmltYXBBdHRhY2tzKjg4LjUqNjApKQ0KfQ0KDQpgYGANCg0KDQpgYGB7cn0NCg0KI1JlbW92ZSB0aGUgIk1pbmltYXBSaWdodENsaWNrcyIgb3V0bGllciB2YWx1ZXMgYW5kIGNvbnZlcnQgdGhlIHZhbHVlcyBpbiB0ZXJtcyBvZiBtaW51dGVzDQoNCnNjIDwtIGZpbHRlcihzYywgc2MkTWluaW1hcFJpZ2h0Q2xpY2tzIDw9IDAuMDAyOCkNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRNaW5pbWFwUmlnaHRDbGlja3MqODguNSo2MCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xvZXNzJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjUsIGFlcyhncm91cCA9IDEpKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnTWluaW1hcCBSaWdodCBDbGlja3MgUGVyZm9ybWVkIHBlciBNaW51dGUnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCAxNSwgMSkpKQ0KDQpmb3IobGVhZ3VlIGluIHJhbmspIHsNCiAgcHJpbnQobGVhZ3VlKQ0KICBwcmludChzdW1tYXJ5KGZpbHRlcihzYywgc2MkTGVhZ3VlID09IGxlYWd1ZSkkTWluaW1hcFJpZ2h0Q2xpY2tzKjg4LjUqNjApKQ0KfQ0KDQpgYGANCg0KDQo+IEluIGJvdGggY2FzZXMsIHRoZXJlJ3MgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB0aGF0IGFwcGVhcnMgdG8gaW5jcmVhc2Ugd2l0aCB0aGUgbGVhZ3Vlcy4gRm9yICpNaW5pbWFwQXR0YWNrcyosIHRoZSBzbG9wZSBvZiB0aGUgTE9FU1MgbGluZSBpbmNyZWFzZXMgYXQgdGhlIHVwcGVyIGxlYWd1ZXMuIFRoZSBsYXJnZSBpbmNyZWFzZSBpbiB0aGUgbWVkaWFuIGJldHdlZW4gTWFzdGVyIGFuZCBHcmFuZE1hc3RlciAoMC40ODY3IHRvIDEuMjc3KSBzaG93cyB0aGUgaW1wb3J0YW5jZSBvZiB1c2luZyB0aGUgbWluaW1hcCBpbiBhdHRhaW5pbmcgdGhlIGhpZ2hlc3QgbGV2ZWwgb2Ygc2tpbGwuIE9uIHRoZSBvdGhlciBlbmQsIHRoZSBtZWRpYW4gYWxzbyBjaGFuZ2VzIHNpZ25pZmljYW50bHkgZnJvbSBCcm9uemUgdG8gU2lsdmVyIChvIHRvIDAuMDc0MzQpLCB3aGljaCBzdXBwb3J0cyB0aGUgYmVsaWVmIHRoYXQgYXQgbGVhc3Qgc29tZSBtaW5pbWFwIHVzYWdlIGNhbiBiZSBlbm91Z2ggdG8gYmUgcHJvbW90ZWQgb3V0IG9mIEJyb256ZSBMZWFndWUuIEl0IHNlZW1zIHRoYXQgbWFueSBwbGF5ZXJzIGZyb20gQnJvbnplIGFsbCB0aGUgd2F5IHRvIFBsYXRpbnVtIExlYWd1ZSBkb24ndCBpc3N1ZSBhdHRhY2sgY29tbWFuZHMgdGhyb3VnaCB0aGUgbWluaW1hcCBvciBkbyBzbyB2ZXJ5IHJhcmVseS4gRm9yICpNaW5pbWFwUmlnaHRDbGlja3MqLCB0aGUgdmFyaWF0aW9uIGJldHdlZW4gbGVhZ3VlcyBpc24ndCBhcyBwcm9ub3VuY2VkLCB3aXRoIGEgcm91Z2hseSBsaW5lYXIgTE9FU1MgbGluZS4gSW4gdGhlIGxvd2VyIGxlYWd1ZXMsIHRoZSBtZWRpYW4gdmFsdWUgb2YgMC44MTAzIGZvciBCcm9uemUgTGVhZ3VlIGluZGljYXRlcyB0aGF0IHRoaXMgaXMgYW4gYWN0aW9uIHRoYXQgbW9zdCBub3ZpY2UgcGxheWVycyBhbHJlYWR5IGtub3cgdG8gZG8uIFRoZXJlZm9yZSB0aGV5IGp1c3QgbmVlZCB0byBsZWFybiB0byBkbyBzbyBtb3JlIG9mdGVuLg0KDQoNCiMjIzMpICpBc3NpZ25Ub0hvdGtleXMqIGFuZCAqU2VsZWN0QnlIb3RrZXlzKg0KDQoNCj4gSXQgY291bGQgYmUgYXJndWVkIHRoYXQgbGVhcm5pbmcgdG8gdXNlIGhvdGtleXMgaXMgZXF1YWxseSBlc3NlbnRpYWwgdG8gaW1wcm92aW5nIGF0IFNDMi4gSG90a2V5cyBhcmUgdGhlIGNvbXB1dGVyIGdhbWUgZXF1aXZhbGVudCBvZiBrZXlib2FyZCBzaG9ydGN1dHMsIGFuZCBieSBub3QgdXNpbmcgdGhlbSwgb25lIHdvdWxkIGJlIHJlZHVjZWQgdG8gcGxheWluZyB0aGUgZ2FtZSB3aXRoIG9ubHkgYSBtb3VzZS4gVG8gZ2l2ZSBhbiBpZGVhIG9mIGhvdyBpbmVmZmljaWVudCBhbmQgdGltZS1jb25zdW1pbmcgdGhhdCB3b3VsZCBiZSwgaW1hZ2luZSBoYXZpbmcgdG8gdHlwZSBieSB1c2luZyBhIG1vdXNlIHRvIGNsaWNrIGVhY2ggbGV0dGVyIG9uIGEgdmlydXRhbCBrZXlib2FyZCBhdCB0aGUgYm90dG9tIG9mIHRoZSBjb21wdXRlciBzY3JlZW4uIFRoaXMgYW5hbG9neSBpcyBub3QgZmFyIGZyb20gdGhlIHRydXRoIHNpbmNlIG1vc3Qgb2YgdGhlIGJ1dHRvbnMgY29ycmVzcG9uZGluZyB0byBhY3Rpb25zIGluIHRoZSBnYW1lIGFyZSBsb2NhdGVkIGF0IHRoZSBib3R0b20gb2YgdGhlIHVzZXIgaW50ZXJmYWNlLg0KDQoNCj4gRWFjaCBwb3NzaWJsZSBjb21tYW5kIGluIHRoZSBnYW1lIGhhcyBhbiBhc3NvY2lhdGVkIGRlZmF1bHQgaG90a2V5LiBJbiBhZGRpdGlvbiwgcGxheWVycyBjYW4gYXNzaWduIHNwZWNpZmljIGdyb3VwcyBvZiB1bml0cyBhbmQgYnVpbGRpbmdzIHRvIG9uZSBvZiB0aGUgbnVtYmVyIGtleXMuIFRoZXJlZm9yZSwgYnkgcHJlc3NpbmcgYSBudW1iZXIga2V5LCBhIHBsYXllciBjYW4gaW1tZWRpYXRlbHkgc2VsZWN0IHNldmVyYWwgdW5pdHMgb3IgYnVpbGRpbmdzIHNpbXVsdGFuZW91c2x5LCBubyBtYXR0ZXIgd2hlcmUgdGhleSBhcmUgb24gdGhlIG1hcCBvciBob3cgZmFyIGFwYXJ0IHRoZXkgYXJlLCBhbmQgaXNzdWUgY29tbWFuZHMgdG8gdGhlbS4gVGhlIHZhcmlhYmxlICpBc3NpZ25Ub0hvdGtleXMqIGluZGljYXRlcyBob3cgb2Z0ZW4gYSBwbGF5ZXIgbWFrZXMgdGhlc2UgYXNzaWdubWVudHMsIHdoZXJlYXMgKlNlbGVjdEJ5SG90a2V5cyogaW5kaWNhdGVzIGhvdyBvZnRlbiBhIHBsYXllciBjb250cm9scyB0aGVzZSBwcmV2aW91c2x5IGFzc2lnbmVkIGdyb3VwcyB1c2luZyB0aGUgaG90a2V5cy4NCg0KDQpgYGB7cn0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRBc3NpZ25Ub0hvdGtleXMqODguNSo2MCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdIb3RrZXkgQXNzaWdubWVudHMgTWFkZSBwZXIgTWludXRlJykNCg0KYGBgDQoNCg0KYGBge3J9DQoNCiNSZW1vdmUgb3V0bGllciB2YWx1ZXMgZm9yIHRoZSAiQXNzaWduVG9Ib3RrZXlzIiB2YXJpYWJsZQ0KDQpzYyA8LSBmaWx0ZXIoc2MsIHNjJEFzc2lnblRvSG90a2V5cyo4OC41KjYwIDw9IDYuNSkNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRBc3NpZ25Ub0hvdGtleXMqODguNSo2MCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xvZXNzJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjUsIGFlcyhncm91cCA9IDEpKSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnSG90a2V5IEFzc2lnbm1lbnRzIE1hZGUgcGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsNywgMC41KSkpDQoNCmZvcihsZWFndWUgaW4gcmFuaykgew0KICBwcmludChsZWFndWUpDQogIHByaW50KHN1bW1hcnkoZmlsdGVyKHNjLCBzYyRMZWFndWUgPT0gbGVhZ3VlKSRBc3NpZ25Ub0hvdGtleXMqODguNSo2MCkpDQp9DQoNCmBgYA0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3Qoc2MsIGFlcyhzYyRMZWFndWUsIHNjJFNlbGVjdEJ5SG90a2V5cyo4OC41KjYwKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvciA9ICdibHVlJywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ0hvdGtleSBHcm91cHMgU2VsZWN0ZWQgcGVyIE1pbnV0ZScpDQoNCmBgYA0KDQoNCj4gQWZ0ZXIgcmVtb3ZpbmcgdGhvc2Ugb3V0bGllcnMsIHRoZXJlIGFyZSBzdGlsbCBzb21lIGRhdGEgcG9pbnRzIHdpdGggb3V0bGllciB2YWx1ZXMgZm9yIHRoZSAqU2VsZWN0QnlIb3RrZXlzKiB2YXJpYWJsZSAoMzIvMzM5NSkgdGhhdCBzaG91bGQgYmUgcmVtb3ZlZCBhcyB3ZWxsLg0KDQoNCmBgYHtyfQ0KDQojUmVtb3ZlIG91dGxpZXIgdmFsdWVzIGZvciB0aGUgIlNlbGVjdEJ5SG90a2V5cyIgdmFyaWFibGUgYW5kIGNvbnZlcnQgdGhlIHZhbHVlcyBpbiB0ZXJtcyBvZiBtaW51dGVzDQoNCnNjIDwtIGZpbHRlcihzYywgc2MkU2VsZWN0QnlIb3RrZXlzKjg4LjUqNjAgPCAxNTApDQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkU2VsZWN0QnlIb3RrZXlzKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS4yLCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ0hvdGtleSBHcm91cHMgU2VsZWN0ZWQgcGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsMjAwLCAxMCkpKQ0KDQpmb3IobGVhZ3VlIGluIHJhbmspIHsNCiAgcHJpbnQobGVhZ3VlKQ0KICBwcmludChzdW1tYXJ5KGZpbHRlcihzYywgc2MkTGVhZ3VlID09IGxlYWd1ZSkkU2VsZWN0QnlIb3RrZXlzKjg4LjUqNjApKQ0KfQ0KDQpgYGANCg0KDQo+IEluIEJyb256ZSBhbmQgU2lsdmVyIExlYWd1ZSwgdGhlIG1lZGlhbiBhbmQgbWVhbiB2YWx1ZXMgZm9yICpBc3NpZ25Ub0hvdGtleXMqIGFyZSBjbG9zZSB0byAxLiBGcm9tIG15IGV4cGVyaWVuY2Ugd2l0aCB0aGUgZ2FtZSwgdGhpcyByZWZsZWN0cyB0aGUgaGFiaXQgb2YgYXNzaWduaW5nIG9uZSdzIGVudGlyZSBhcm15IHRvIG9uZSBob3RrZXkgYW5kIHVwZGF0aW5nIHRoZSBhc3NpZ25tZW50IGJ5IGFkZGluZyBuZXdseSBwcm9kdWNlZCB1bml0cyB0byB0aGF0IGhvdGtleSBncm91cC4gUGxheWVycyB3aG8gY29udHJvbCB0aGVpciBlbnRpcmUgYXJteSBhcyBhIHNpbmdsZSBsYXJnZSBtYXNzIG1vc3RseSBkbyBzbyBiZWNhdXNlIHRoZXkgbGFjayB0aGUgbXVsdGl0YXNraW5nIHNraWxscyB0byBlZmZlY3RpdmVseSBjb250cm9sIG11bHRpcGxlIGdyb3VwcyBhdCBhIHRpbWUuIEhvd2V2ZXIsIHRoaXMgc2V0dXAgbWFrZXMgaXQgZGlmZmljdWx0IGZvciB0aGVtIHRvIHNwbGl0IHRoZWlyIHVuaXRzIHVwIHRvIGRlYWwgd2l0aCB0aHJlYXRzIG9jY3VycmluZyBzaW11bHRhbmVvdXNseSBpbiBkaWZmZXJlbnQgcGxhY2VzLiBUaGUgbWVkaWFuIGFuZCBtZWFuIHZhbHVlcyBmb3IgKlNlbGVjdEJ5SG90a2V5cyogaW4gdGhlIGxvd2VyIGxlYWd1ZXMgYXJlIHNpbWlsYXJseSBsb3cgZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlcmUncyBubyBuZWVkIHRvIG1ha2UgdG9vIG1hbnkgaG90a2V5IHNlbGVjdGlvbnMgd2hlbiB5b3VyIGVudGlyZSBhcm15IGlzIGFzc2lnbmVkIHRvIG9ubHkgb25lIG9yIHR3byBob3RrZXkgZ3JvdXBzLiBUaGlzIHR5cGUgb2YgaGFiaXQgc2VlbXMgdG8gZGlzYXBwZWFyIGFzIG9uZSBwcm9ncmVzc2VzIHRocm91Z2ggdGhlIGxlYWd1ZXMuIENvbXBhcmluZyB0aGUgbG93ZXIgbGVhZ3VlcyB0byB0aGUgaGlnaGVyIG9uZXMgaW5kaWNhdGVzIHRoYXQgdGhlc2UgdmFyaWFibGVzIGFyZSBtb3JlIGltcG9ydGFudCBpbiBiZWluZyBwcm9tb3RlZCBmcm9tIHRoZSBtaWRkbGUgbGVhZ3Vlcy4gVGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhlIERpYW1vbmQsIE1hc3RlciwgYW5kIEdyYW5kTWFzdGVyIExlYWd1ZSBkaXN0cmlidXRpb25zIGFyZSBxdWl0ZSBwcm9ub3VuY2VkLiBUaGlzIGlsbHVzdHJhdGVzIHRoZSBpbXBvcnRhbmNlIG9mIG11bHRpdGFza2luZyBpbiBTQzIsIGFuZCBzcGVjaWZpY2FsbHksIHRoZSBpbXBvcnRhbmNlIG9mIG1haW50YWluaW5nIG1hbnkgaG90a2V5IGdyb3VwcyBhbmQgY29udGludW91c2x5IHNlbGVjdGluZyB0aGVtIHRvIGlzc3VlIG9yZGVycyB0byBhdHRhY2sgYW5kIGRlZmVuZC4NCg0KDQojIyM0KSAqVG90YWxNYXBFeHBsb3JlZCoNCg0KDQo+IFRoZSBuZWVkIGZvciBpbnRlbGxpZ2VuY2UgZ2F0aGVyaW5nIGR1cmluZyBiYXR0bGVzIHNob3VsZCBiZSBhcHBhcmVudC4gQXMgYSBjb25zZXF1ZW5jZSBvZiBnYW1lcyBoYXBwZW5pbmcgdW5kZXIgdGhlIGZvZyBvZiB3YXIsIHBsYXllcnMgaGF2ZSBhbiBpbmNlbnRpdmUgdG8gY29uc3RhbnRseSBzY291dCBhbmQgb2JzZXJ2ZSB0aGUgb3Bwb3NpbmcgcGxheWVyJ3MgYWN0aW9ucyB3aGlsZSBoaWRpbmcgdGhlaXIgb3duIGFjdGlvbnMgZnJvbSBlbmVteSBzY291dHMuIEFzIHByZXZpb3VzbHkgbWVudGlvbmVkLCBwbGF5ZXJzIGNhbiBkZWNpZGUgdG8gcGVyZm9ybSBzbmVhayBhdHRhY2tzIGVhcmx5IGluIGEgZ2FtZSB0byB3aW4gdGhlIGdhbWUgcXVpY2tseSBhbmQgZGVjaXNpdmVseS4gRXZlbiBpbiBzbG93ZXIgcGFjZWQgZ2FtZXMsIG9uZSBzaG91bGQgYmUgYXdhcmUgb2Ygd2hhdCBraW5kIG9mIGFybXkgdGhlIG9wcG9uZW50IGlzIHByb2R1Y2luZyBhbmQgd2hlcmUgdGhhdCBhcm15IGlzIG1vdmluZy4gSW4gU0MyLCBlYWNoIGFybXkgdW5pdCBoYXMgYSAicm9jaywgcGFwZXIsIHNjaXNzb3JzIiByZWxhdGlvbnNoaXAgd2l0aCBvdGhlciB0eXBlcyB0eXBlcyBvZiB1bml0cywgbWVhbmluZyB0aGF0IGVhY2ggdW5pdCBpcyBlZmZlY3RpdmUgYXQgZmlnaHRpbmcgY2VydGFpbiB0eXBlcyBvZiB1bml0cyB3aGlsZSBiZWluZyBpbmVmZmVjdGl2ZSBhZ2FpbnN0IG90aGVyIHR5cGVzIG9mIHVuaXRzLiBUaHVzLCBieSBrZWVwaW5nIHdhdGNoIG9mIHRoZSBvcHBvbmVudCdzIGFybXksIG9uZSBjYW4gcmVzcG9uZCBieSBwcm9kdWNpbmcgdGhlIHR5cGVzIG9mIHVuaXRzIHRoYXQgYXJlIGVmZmVjdGl2ZSBhZ2FpbnN0IHRoZSBvcHBvc2luZyBhcm15Lg0KDQoNCj4gSW4gU0MyLCB0aGUgYmF0dGxlZmllbGQgaXMgZGl2aWRlZCBpbnRvIHNxdWFyZXMgdGhhdCBhcmUgYWJvdXQgdGhlIHNpemUgb2YgdGhlIG1vdXNlIHBvaW50ZXIuIFRoZSAqVG90YWxNYXBFeHBsb3JlZCogbWV0cmljIG1lYXN1cmVzIGhvdyBtYW55IDI0eDI0IGdyaWRzIG9mIHRoZXNlIHNxdWFyZXMgYSBwbGF5ZXIgaGFzIGV4cGxvcmVkIHBlciB0aW1lc3RhbXAuIER1cmluZyB0aGUgY291cnNlIG9mIGEgZ2FtZSwgcGxheWVycyB3aWxsIG5hdHVyYWxseSBleHBsb3JlIHRlcnJpdG9yeSBieSB2aXJ0dWUgb2YgZXhwYW5kaW5nIHRvIG5ldyBiYXNlcyB0byBjb2xsZWN0IG1vcmUgcmVzb3VyY2VzIGFuZCBzZW5kaW5nIHRoZWlyIGFybXkgdG8gYXR0YWNrIHRoZSBvcHBvbmVudC4gSG93ZXZlciwgYSBza2lsbGVkIHBsYXllciB3aWxsIGdvIGJleW9uZCB0aGlzIGFuZCBpbnRlbnRpb25hbGx5IHNlbmQgdW5pdHMgdG8gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBtYXAgdG8gY29uZmlybSB3aGF0IHRoZSBvcHBvbmVudCBpcyBkb2luZyBhbmQgdG8gbWFrZSBzdXJlIHRoYXQgbm8gc25lYWsgYXR0YWNrcyBhcmUgaW5jb21pbmcuIFRpbWVseSBpbmZvcm1hdGlvbiByZWNlaXZlZCBmcm9tIHNjb3V0aW5nIGNhbiBtZWFuIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gdmljdG9yeSBhbmQgZGVmZWF0IGluIG1hbnkgZ2FtZXMuDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkVG90YWxNYXBFeHBsb3JlZCo4OC41KjYwKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvciA9ICdibHVlJywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ051bWJlciBvZiAyNHgyNCBNYXAgR3JpZHMgRXhwbG9yZWQgcGVyIE1pbnV0ZScpKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCw1LCAwLjI1KSkpDQoNCmBgYA0KDQoNCj4gVGhlIGxlYWd1ZSBkaXN0cmlidXRpb24gbWVkaWFucyBzZWVtIHRvIGJlIGluY3JlYXNpbmcgYWxtb3N0IGxpbmVhcmx5LCBidXQgSSdkIGxpa2UgdG8gcmVtb3ZlIHRoZSBmZXcgb3V0bGllciB2YWx1ZXMgKDIvMzM5NSkgZ3JlYXRlciB0aGFuIDQuMDAgdG8gbWFrZSBzdXJlIHRoZXJlIGlzbid0IGEgcG9seW5vbWlhbCBpbmNyZWFzZSBiZWluZyBvYnNjdXJlZC4NCg0KDQpgYGB7cn0NCg0KI1JlbW92ZSBvdXRsaWVyIHZhbHVlcyBmb3IgdGhlICJUb3RhbE1hcEV4cGxvcmVkIiB2YXJpYWJsZSBhbmQgY29udmVydCB0aGUgdmFsdWVzIGluIHRlcm1zIG9mIG1pbnV0ZXMNCg0Kc2MgPC0gZmlsdGVyKHNjLCBzYyRUb3RhbE1hcEV4cGxvcmVkIDwgMC4wMDA3NCkNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRUb3RhbE1hcEV4cGxvcmVkKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS4yLCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ051bWJlciBvZiAyNHgyNCBNYXAgR3JpZHMgRXhwbG9yZWQgcGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsNSwgMC4yNSkpKQ0KDQpmb3IobGVhZ3VlIGluIHJhbmspIHsNCiAgcHJpbnQobGVhZ3VlKQ0KICBwcmludChzdW1tYXJ5KGZpbHRlcihzYywgc2MkTGVhZ3VlID09IGxlYWd1ZSkkVG90YWxNYXBFeHBsb3JlZCo4OC41KjYwKSkNCn0NCg0KYGBgDQoNCg0KPiBUaGVyZSdzIGFzIHJlbGF0aXZlbHkgbGFyZ2UgaW5jcmVhc2UgaW4gdGhlIG1lZGlhbiBnb2luZyBmcm9tIEJyb256ZSB0byBTaWx2ZXIgTGVhZ3VlIHdoaWNoIGluZGljYXRlcyB0aGF0IFNpbHZlciBMZWFndWUgcGxheWVycyBsZWF2ZSB0aGVpciBiYXNlcyBtb3JlIG9mdGVuLCB0aG91Z2ggaXQncyB1bmNsZWFyIHdoZXRoZXIgdGhpcyBpcyB0aGUgcmVzdWx0IG9mIHB1cnBvc2VmdWwgc2NvdXRpbmcgb3IganVzdCBhIGNvbnNlcXVlbmNlIG9mIGJlaW5nIG1vcmUgYWN0aXZlIG9uIHRoZSBiYXR0bGVmaWVsZCB3aGVuIHBlcmZvcm1pbmcgb3RoZXIgYWN0aW9ucy4gUGFzdCBCcm9uemUgTGVhZ3VlLCBpdCBsb29rcyBsaWtlIHRoZSBkaXN0cmlidXRpb25zIGFyZSByZWxhdGl2ZWx5IHVudGlsIHRoZSBtaWRkbGUsIGFjY29yZGluZyB0byB0aGUgTE9FU1MgbGluZS4gS25vd2luZyB0byBzY291dCBzZWVtcyB0byBiZSBhIGEgc2tpbGwgdGhhdCdzIGFjcXVpcmVkIHdoZW4gdHJhbnNpdGlvbmluZyBmcm9tIHRoZSBHb2xkIHRvIFBsYXRpbnVtIExlYWd1ZXMuIFRoZSB0cmFuc2l0aW9uIGZyb20gTWFzdGVyIHRvIEdyYW5kTWFzdGVyIExlYWd1ZSBzaG93cyBhbm90aGVyIHJlbGF0aXZlbHkgbGFyZ2UgbWVkaWFuIGluY3JlYXNlLCB3aGljaCBpbmRpY2F0ZXMgdGhhdCBzY291dGluZyBpcyBhIGNydWNpYWwgc2tpbGwgdG8gYWNxdWlyZSBpbiBvcmRlciB0byByZWFjaCB0aGUgaGlnaGVzdCBza2lsbCBsZXZlbC4NCg0KDQojIyM1KSAqVW5pcXVlVW5pdHNNYWRlKg0KDQoNCj4gQXQgZmlyc3QsIEkgdGhvdWdodCB0aGlzIHZhcmlhYmxlIG1lYXN1cmVkIHRoZSByYXRlIHRoYXQgcGxheWVycyB3ZXJlIHByb2R1Y2luZyB1bml0cy4gVGhpcyB3b3VsZCBiZSBhIG1lYXN1cmUgb2YgYSBwbGF5ZXIncyBtYWNyb21hbmFnZW1lbnQgYW5kIGJlaW5nIGFibGUgdG8gZWZmaWNpZW50bHkgc3BlbmQgaGlzIG9yIGhlciByZXNvdXJjZXMuIEhvd2V2ZXIsIHVwb24gY29udmVydGluZyB0aGUgdmFsdWVzIHRvIHJlZmxlY3QgbWludXRlcyByYXRoZXIgdGhhbiB0aW1lc3RhbXBzLCBJIHNhdyB0aGF0IHRoZSB2YWx1ZXMgd2VyZSBtdWNoIHRvbyBsb3csIGNvbnNpZGVyaW5nIHRoYXQgZ2FtZXMgdHlwaWNhbGx5IGxhc3QgZm9yIDE1LTI1IG1pbnV0ZXMgYW5kIGFybWllcyBvZnRlbiBjb25zaXN0IG9mIGRvemVucyBvZiB1bml0cy4gDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkVW5pcXVlVW5pdHNNYWRlKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnVW5pcXVlIFVuaXRzIE1hZGUgcGVyIE1pbnV0ZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDEuMSwgMC4wNSkpKQ0KDQpgYGANCg0KDQo+IFRoZXNlIHZhbHVlcyB3b3VsZCBpbXBseSB0aGF0IG9uIGF2ZXJhZ2UsIG9ubHkgYSBoYW5kZnVsIG9mIHVuaXRzIGFyZSBwcm9kdWNlZCBwZXIgZ2FtZSwgd2hpY2ggaXMgY2xlYXJseSBub3QgY29ycmVjdC4gSSB0aGVuIHRyaWVkIG11bHRpcGx5aW5nIHRoaXMgdmFyaWFibGUgYnkgKk1heFRpbWVTdGFtcCosIHdoaWNoIGluZGljYXRlcyBob3cgbWFueSB0aW1lc3RhbXBzIGVhY2ggZ2FtZSBjb250YWluZWQsIHdoaWNoIHdvdWxkIGluZGljYXRlIHRoZSBkdXJhdGlvbiBvZiBlYWNoIGdhbWUuIFRoaXMgc2hvdWxkIHByb3ZpZGUgdGhlIHRvdGFsIG51bWJlciBvZiB1bmlxdWUgdW5pdHMgcHJvZHVjZWQgcGVyIGdhbWUuDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkVW5pcXVlVW5pdHNNYWRlKnNjJE1heFRpbWVTdGFtcCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdVbmlxdWUgVW5pdHMgTWFkZSBwZXIgR2FtZScpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsMTUsIDEpKSkNCg0KYGBgDQoNCg0KPiBOb3RpY2UgdGhhdCBhbGwgb2YgdGhlIHZhbHVlcyBmYWxsIGJldHdlZW4gMiBhbmQgMTMuIEdpdmVuIHRoaXMgcmFuZ2UsIGl0IGlzIG1vc3QgbGlrZWx5IHRoYXQgdGhpcyB2YXJpYWJsZSBpcyB0aGUgbnVtYmVyIG9mIGRpZmZlcmVudCB0eXBlcyBvZiB1bml0cyBwcm9kdWNlZCBwZXIgZ2FtZSBkaXZpZGVkIGJ5IHRoZSB0b3RhbCBsZW5ndGggb2YgdGhlIGdhbWUgY29uc2lkZXJpbmcgdGhhdCBlYWNoIHBsYXllciBjYW4gb25seSBjaG9vc2UgZnJvbSBhdCBtb3N0IDE1IHR5cGVzIG9mIHVuaXRzIHRvIHByb2R1Y2UuIEEgdmFsdWUgb2YgMiB3b3VsZCBpbmRpY2F0ZSB0aGF0IHRoZSBwbGF5ZXIgcHJvZHVjZWQgbm90aGluZyBidXQgd29ya2VycyBhbmQgdGhlIG1vc3QgYmFzaWMsIGxvdy10ZWNoIGF0dGFja2luZyB1bml0LiBHaXZlbiBteSBuZXcgdW5kZXJzdGFuZGluZyBvZiB0aGlzIHZhcmlhYmxlLCBJIGRlY2lkZWQgdGhhdCBJIHdvdWxkIG5vdCB1c2UgaXQgYW55IGZ1cnRoZXIgZHVyaW5nIHRoaXMgcHJvamVjdC4gV2hpbGUgbWFraW5nIGEgZGl2ZXJzZSBhcm15IGNvbXByaXNlZCBvZiB2YXJpb3VzIHR5cGVzIG9mIHVuaXRzIGNhbiBiZSBzZWVuIGFzIGEgbWVhc3VyZSBvZiBob3cgd2VsbCBhIHBsYXllciBjYW4gYWRhcHQgdG8gY2hhbmdpbmcgc2l0dWF0aW9ucyBpbiB0aGUgZ2FtZSBieSBhZGp1c3RpbmcgdGhlIGNvbXBvc2l0aW9uIG9mIGhpcyBvciBoZXIgYXJteSwgaXQgaXMgYWxzbyB0cnVlIHRoYXQgKlVuaXF1ZVVuaXRzTWFkZSogaXMgbmF0dXJhbGx5IGNvcnJlbGF0ZWQgd2l0aCBnYW1lIGxlbmd0aC4gVGhpcyBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCB0aGUgbW9yZSBhZHZhbmNlZCwgaGktdGVjaCB1bml0cyBjYW5ub3QgYmUgcHJvZHVjZWQgZWFybHkgaW4gdGhlIGdhbWUgZHVlIHRvIHRoZWlyIGhpZ2ggY29zdHMgYW5kIHByZXJlcXVpc2l0ZSBpbmZyYXN0cnVjdHVyZSByZXF1aXJlbWVudHMgKHRoaXMgaWRlYSBpcyBleHBsYWluZWQgaW4gbW9yZSBkZXRhaWwgaW4gdGhlIG5leHQgc2VjdGlvbikuIEFzIG1lbnRpb25lZCBiZWZvcmUsIGdhbWUgbGVuZ3RoIGlzIGluIHBhcnQgZGV0ZXJtaW5lZCBieSBjb25zY2lvdXMgZGVjaXNpb25zIG1hZGUgYnkgZWFjaCBwbGF5ZXIsIHNvIGl0IHdvdWxkIG5vdCBiZSBhcHByb3ByaWF0ZSB0byBpbnRlcnByZXQgdGhpcyBtZXRyaWMgYXMgcHVyZSBtZWFzdXJlIG9mIHNraWxsLg0KDQoNCiMjIzYpICpDb21wbGV4QWJpbGl0aWVzVXNlZCogLyAqQ29tcGxleFVuaXRzTWFkZSoNCg0KDQo+IEluIFNDMiwgdGhlcmUgaXMgYSBoZWlyYXJjaHkgaW4gd2hpY2ggYWxsIHRoZSBkaWZmZXJlbnQgdHlwZXMgb2YgYnVpbGRpbmdzIGFuZCBhcm15IHVuaXRzIGJlbG9uZywgY2FsbGVkIHRoZSAidGVjaCB0cmVlIi4gSW4gdGhlIHRlY2ggdHJlZSwgYnVpbGRpbmdzIGFuZCB1bml0cyBmdXJ0aGVyIHVwIHRoZSB0cmVlIGNhbiBvbmx5IGJlIGFjY2Vzc2VkIG9uY2UgY2VydGFpbiBidWlsZGluZ3MgZnVydGhlciBkb3duIGluIHRoZSB0ZWNoIHRyZWUgaGF2ZSBiZWVuIGNvbnN0cnVjdGVkLiBUaGlzIG5ldHdvcmsgb2YgZGVwZW5kZW5jaWVzIGVuc3VyZXMgdGhhdCB0aGUgaW5leHBlbnNpdmUsIGxvdy10ZWNoIGJ1aWxkaW5ncyBhbmQgdW5pdHMgYXJlIGFjY2Vzc2libGUgcmVsYXRpdmVseSBlYXJseSBpbiB0aGUgZ2FtZSwgd2hlcmVhcyB0aGUgbW9zdCBwb3dlcmZ1bCBhbmQgZXhwZW5zaXZlIHVuaXRzIGFyZSByZXNlcnZlZCBmb3IgbGF0ZXIgaW4gdGhlIGdhbWUuIE9mdGVuLCB0aGVzZSB1bml0cyBoYXZlIGRldmFzdGF0aW5nIGFiaWxpdGllcyB0aGF0IGNhbiBiZSB1c2VkIHJlcGVhdGVkbHkgKHdpdGggYSBwcmVkZXRlcm1pbmVkIHJlY2hhcmdlIHBlcmlvZCBpbiBiZXR3ZWVuIHVzZXMpIHRvIGRldmFzdGF0ZSBhbiBvcHBvc2luZyBhcm15IGlmIHVzZWQgY29ycmVjdGx5LiBUaGlzIGRlcGVuZHMgb24gdGhlIHBsYXllciBtYW51YWxseSBjbGlja2luZyBvbiB0aGUgaW50ZW5kZWQgdGFyZ2V0IGluIGFuIGFjY3VyYXRlIGFuZCB0aW1lbHkgbWFubmVyLiBPdGhlcndpc2UsIHRoZSBhdHRhY2sgbWlzc2VzIGl0cyB0YXJnZXQgYW5kIGlzIHdhc3RlZC4NCiANCg0KIVtTZXZlcmFsIHVuaXRzIGhhdmUgYWN0aXZhdGVkIGFiaWxpdGllcyB0aGF0IG11c3QgYmUgdHJpZ2dlcmVkIGF0IHRoZSByaWdodCB0aW1lIG9yIHRhcmdldGVkIGFiaWxpdGllcyB0aGF0IHJlcXVpcmUgdGhlIHBsYXllciB0byBkaXJlY3RseSBjbGljayBvbiB0aGUgYXJlYSBvciB1bml0IHRvIGJlIHRhcmdldGVkLl0oY29tcGxleC5qcGcpDQoNCg0KPiAqQ29tcGxleEFiaWxpdGllc1VzZWQqIG1lYXN1cmVzIGhvdyBvZnRlbiBhIHBsYXllciB1c2VzIHN1Y2ggdGFyZ2V0ZWQgYWJpbGl0aWVzLCB3aGVyZWFzICpDb21wbGV4VW5pdHNNYWRlKiBtZWFzdXJlcyB0aGUgcmF0ZSBhdCB3aGljaCB0aGVzZSBhZHZhbmNlZCB1bml0cyBhcmUgcHJvZHVjZWQuIFByb3Blcmx5IHV0aWxpemluZyB0aGVzZSB1bml0cyBhbmQgYWJpbGl0aWVzIHJlcXVpcmVzIGFub3RoZXIgbGF5ZXIgb2YgbWljcm9tYW5hZ2VtZW50IG9uIHRvcCBvZiBldmVyeXRoaW5nIGVsc2UgdGhhdCBhIHBsYXllciBuZWVkcyB0byBhdHRlbmQgdG8gZHVyaW5nIGEgZ2FtZS4gVGhlcmVmb3JlLCBvbmUgd291bGQgdGhpbmsgdGhhdCBoaWdoZXIgc2tpbGxlZCBwbGF5ZXJzIHdpbGwgdXNlIHRoZW0gd2l0aCBncmVhdGVyIGZyZXF1ZW5jeS4NCg0KDQpgYGB7cn0NCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRDb21wbGV4QWJpbGl0eVVzZWQqODguNSo2MCkpICsgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3IgPSAnYmx1ZScsIGZpbGwgPSAncHVycGxlJywgYWxwaGEgPSAwLjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdUYXJnZXRlZCBBYmlsaXRpZXMgVXNlZCBwZXIgTWludXRlJykNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRDb21wbGV4VW5pdHNNYWRlKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ0xlYWd1ZScsIHkgPSAnQWR2YW5jZWQgVW5pdHMgUHJvZHVjZWQgcGVyIE1pbnV0ZScpDQoNCmBgYA0KDQoNCj4gQWdhaW4sIHRoZSBsb3dlciBsZWFndWUgZGlzdHJpYnV0aW9ucyBzZWVtIGxpa2UgdGhleSBtaWdodCBiZSBvYnNjdXJlZCBkdWUgdG8gb3V0bGllcnMgKDk5LzMzOTUgZm9yICpDb21wbGV4QWJpbGl0eVVzZWQqIGFuZCAzLzMzOTUgZm9yICpDb21wbGV4VW5pdHNNYWRlKikuDQoNCg0KYGBge3J9DQoNCiNSZW1vdmUgb3V0bGllciB2YWx1ZXMgZm9yICJDb21wbGV4QWJpbGl0eVVzZWQiIGFuZCBjb252ZXJ0IHRoZSB2YWx1ZXMgaW4gdGVybXMgb2YgbWludXRlcw0KDQpzYyA8LSBmaWx0ZXIoc2MsIHNjJENvbXBsZXhBYmlsaXR5VXNlZCA8IDAuMDAxODgpDQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkQ29tcGxleEFiaWxpdHlVc2VkKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS4yLCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ1RhcmdldGVkIEFiaWxpdGllcyBVc2VkIHBlciBNaW51dGUnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDEwLCAwLjUpKSkNCg0KZm9yKGxlYWd1ZSBpbiByYW5rKSB7DQogIHByaW50KGxlYWd1ZSkNCiAgcHJpbnQoc3VtbWFyeShmaWx0ZXIoc2MsIHNjJExlYWd1ZSA9PSBsZWFndWUpJENvbXBsZXhBYmlsaXR5VXNlZCo4OC41KjYwKSkNCn0NCg0KYGBgDQoNCg0KYGBge3J9DQoNCiNSZW1vdmUgb3V0bGllciB2YWx1ZXMgZm9yICJDb21wbGV4VW5pdHNNYWRlIiBhbmQgY29udmVydCB0aGUgdmFsdWVzIGluIHRlcm1zIG9mIG1pbnV0ZXMNCg0Kc2MgPC0gZmlsdGVyKHNjLCBzYyRDb21wbGV4VW5pdHNNYWRlIDwgMC4wMDA3NSkNCg0KZ2dwbG90KHNjLCBhZXMoc2MkTGVhZ3VlLCBzYyRDb21wbGV4VW5pdHNNYWRlKjg4LjUqNjApKSArIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG9yID0gJ2JsdWUnLCBmaWxsID0gJ3B1cnBsZScsIGFscGhhID0gMC41KSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsb2VzcycsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS4yLCBhZXMoZ3JvdXAgPSAxKSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdMZWFndWUnLCB5ID0gJ0FkdmFuY2VkIFVuaXRzIFByb2R1Y2VkIHBlciBNaW51dGUnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDQsIDAuMjUpKSkNCg0KZm9yKGxlYWd1ZSBpbiByYW5rKSB7DQogIHByaW50KGxlYWd1ZSkNCiAgcHJpbnQoc3VtbWFyeShmaWx0ZXIoc2MsIHNjJExlYWd1ZSA9PSBsZWFndWUpJENvbXBsZXhVbml0c01hZGUqODguNSo2MCkpDQp9DQoNCmBgYA0KDQoNCg0KPiBJdCBhcHBlYXJzIHRoYXQgdGhlIGxvd2VyIGxlYWd1ZSBkaXN0cmlidXRpb25zIGZvciB0aGVzZSB0d28gdmFyaWFibGVzIHRydWx5IGRvIGxvb2sgdGhhdCwgbWVhbmluZyB0aGF0IHBsYXllcnMgaW4gQnJvbnplIG9yIFNpbHZlciBMZWFndWUgdXNlIHRoZXNlIGFkdmFuY2VkIHVuaXRzIGFuZCB0aGVpciBhYmlsaXRpZXMgZXh0cmVtZWx5IHJhcmVseS4gSW4gZmFjdCwgdGhlIG1lZGlhbiB2YWx1ZXMgb2YgKkNvbXBsZXhVbml0c01hZGUqIGZvciBlYWNoIGRpc3RyaWJ1dGlvbiBpcyAwLiBXaGVuIGxvb2tpbmcgYXQgdGhlIG1lYW5zIGZvciBlYWNoIGxlYWd1ZSwgdGhlcmUncyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGZyb20gQnJvbnplIHRvIE1hc3RlciBMZWFndWUgZm9sbG93ZWQgYnkgYSBzbGlnaHQgZGVjcmVhc2UgaW4gR3JhbmRNYXN0ZXIgTGVhZ3VlLiBUaGVyZWZvcmUsIHRoZXNlIHNlZW0gdG8gYmUgYWRkaXRpb25hbCBleGFtcGxlcyBvZiBza2lsbHMgdGhhdCBhcmUgbm90IHBhcnRpY3VsYXJseSB1c2VkIGluIHRoZSBsb3dlciBsZWFndWVzIGJ1dCBhcmUgdXNlZCBtdWNoIG1vcmUgb2Z0ZW4gYW1vbmcgbW9yZSBza2lsbGVkIHBsYXllcnMuDQoNCg0KIyMjNykgKlVuaXF1ZUhvdGtleXMqDQoNCg0KPiBPcmlnaW5hbGx5LCBJIGhhZCByYW5rZWQgdGhpcyBtZXRyaWMgaW4gc2Vjb25kIHBsYWNlIG9uIHRoZSBza2lsbCBoZWlyYXJjaHkuIEZyb20gdGhlIHZhcmlhYmxlIG5hbWUgYW5kIGRlc2NyaXB0aW9uIGZyb20gdGhlIHJlc2VhcmNoZXJzLCBJIHRob3VnaHQgdGhhdCB0aGlzIHZhcmlhYmxlIG1lYXN1cmVkIGhvdyBvZnRlbiBwbGF5ZXJzIHVzZWQgZGlmZmVyZW50IGhvdGtleXMgdG8gaW5wdXQgY29tbWFuZHMuIEhvd2V2ZXIsIHdoZW4gSSBjb252ZXJ0ZWQgdGhlIHZhcmlhYmxlcyBpbiB0ZXJtcyBvZiBtaW51dGVzLCB0aGUgdmFsdWVzIHdlcmUgbXVjaCB0b28gbG93IHRvIG1ha2Ugc2Vuc2Ugd2hlbiBwbGFjZWQgaW4gdGhlIGNvbnRleHQgb2YgQVBNIGFuZCB0aGUgb3RoZXIgaG90a2V5IHZhcmlhYmxlcy4gRm9yIGV4YW1wbGUsIGhvdyBjb3VsZCBhIEdyYW5kTWFzdGVyIHBsYXllciBiZSB1c2luZyBsZXNzIHRoYW4gb25lIHVuaXF1ZSBob3RrZXkgcGVyIG1pbnV0ZSB3aGVuIGhlIGlzIG1vc3QgbGlrZWx5IHBlcmZvcm1pbmcgb3ZlciAxNTAgYWN0aW9ucyBwZXIgbWludXRlPyBVcG9uIGNvbnN1bHRpbmcgd2l0aCBteSBmcmllbmQsIHdlIGNhbWUgdG8gdGhlIGNvbmNsdXNpb24gdGhhdCB0aGlzIG1ldHJpYyBhY3R1YWxseSBtZWFzdXJlcyB0aGUgcmF0ZSBhdCB3aGljaCBob3RrZXlzIHRoYXQgdGhlIHBsYXllciBjaGFuZ2VkIGZyb20gdGhlIGRlZmF1bHQgc2V0dGluZ3MuIEZvciBleGFtcGxlLCBwbGF5ZXJzIGNhbiBjaGFuZ2UgY2VydGFpbiBob3RrZXkgc2V0dGluZ3Mgc28gdGhhdCBjb21tb25seSB1c2VkIGNvbW1hbmRzIGFyZSBhc3NpZ25lZCB0byBuZWlnaGJvcmluZyBrZXlzIG9uIHRoZSBrZXlib2FyZCB0byBmYWNpbGl0YXRlIHRoZSBpc3N1aW5nIG9mIGNvbW1hbmRzLiBDb252ZXJzZWx5LCBhIHBsYXllciBjYW4gYWxzbyBkZWNpZGUgdG8gcmVhc3NpZ24gYSBsaXR0bGUgdXNlZCBjb21tYW5kIGFzIHRvIGF2b2lkIGFjY2lkZW50YWxseSBwcmVzc2luZyBpdCBkdXJpbmcgaGVjdGljIHBlcmlvZHMgaW4gdGhlIGdhbWUuIEdpdmVuIHRoYXQgdGhlcmUncyBhIHN0ZWVwIGxlYXJuaW5nIGN1cnZlIGludm9sdmVkIGluIHRyYW5zaXRpb25pbmcgZnJvbSBub3QgdXNpbmcgaG90a2V5cyBhdCBhbGwsIGJlZ2lubmluZyB0byB1c2UgdGhlbSwgY29tbWl0dGluZyB0aGVtIHRvIG1lbW9yeSBzbyB0aGF0IG9uZSBjYW4gdXNlIHRoZW0gcXVpY2tseSBhbmQgY29tZm9ydGFibHksIHRvIGZpbmFsbHkgdW5kZXJzdGFuZGluZyBvbmUncyBuZWVkcyBhbmQgY3VzdG9taXppbmcgaGlzIG9yIGhlciBob3RrZXkgc2V0dXAgdG8gbWF0Y2ggdGhhdCBuZWVkLCBJIHdvdWxkIGV4cGVjdCB0aGF0IHRoZXJlIHdvdWxkbid0IGJlIG11Y2ggb2YgYSBkaWZmZXJlbmNlIGluIHRoZSBsb3dlciBsZWFndWUgZGlzdHJpYnV0aW9ucyBmb3IgdGhpcyBtZXRyaWMsIGJ1dCB0aGVyZSB3b3VsZCBiZSBhIHByb25vdW5jZWQgZGlmZmVyZW5jZSBpbiB0aGUgdXBwZXIgbGVhZ3VlIGRpc3RyaWJ1dGlvbnMuDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKHNjJExlYWd1ZSwgc2MkVW5pcXVlSG90a2V5cyo4OC41KjYwKSkgKyBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvciA9ICdibHVlJywgZmlsbCA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuNSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG9lc3MnLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnTGVhZ3VlJywgeSA9ICdVbmlxdWUgSG90a2V5cyBVc2VkIHBlciBNaW51dGUnKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCAyLCAwLjEpKSkNCg0KI0Rpc3BsYXkgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciAiVW5pcXVlSG90a2V5cyIgZm9yIGVhY2ggbGVhZ3VlDQoNCmZvcihsZWFndWUgaW4gcmFuaykgew0KICBwcmludChsZWFndWUpDQogIHByaW50KHN1bW1hcnkoZmlsdGVyKHNjLCBzYyRMZWFndWUgPT0gbGVhZ3VlKSRVbmlxdWVIb3RrZXlzKjg4LjUqNjApKQ0KfQ0KDQpgYGANCg0KDQo+IFRoZSBkYXRhIHNlZW1zIHRvIHN1cHBvcnQgbXkgaW50dWl0aW9uLiBJbiB0aGUgTG93ZXIgTGVhZ3VlcywgdGhlIExPRVNTIGxpbmUgaW5jcmVhc2VzIHF1aXRlIHNsb3dseSwgYnV0IHN1ZGRlbmx5IGluY3JlYXNlcyBpbiBzbG93IHdoZW4gdHJhbnNpdGlvbmluZyBmcm9tIFBsYXRpbnVtIHRvIERpYW1vbmQgTGVhZ3VlLiBJdCBzZWVtcyB0aGF0IHRoaXMgdmFyaWFibGUgaXMgbm90IHNvIGltcG9ydGFudCBmb3IgbGVzcyBza2lsbGVkIHBsYXllciwgYnV0IGl0J3MgYSBkZWNpZGluZyBmYWN0b3IgZm9yIGhvdyBza2lsbGVkIHBsYXllcnMgYXJlIGluIHRoZSB1cHBlciBsZWFndWVzLg0KDQoNCiMjI0FuYWx5c2lzIG9mIEVhY2ggTWV0cmljIHdpdGggUmVzcGVjdCB0byBUaW1lIFZhcmlhYmxlcw0KDQoNCj4gSGF2aW5nIGFuYWx5emVkIGVhY2ggb2YgdGhlIHJlbGV2YW50IHZhcmlhYmxlcyBpbmRpdmlkdWFsbHksIEknZCBsaWtlIHRvIGhhdmUgYSBxdWljayBvdmVydmlldyBvZiB0aGUgY29ycmVsYXRpb25zIGJldHdlZW4gdGhlIHZhcmlvdXMgaW4tZ2FtZSBtZXRyaWNzIGFuZCBlYWNoIHRpbWUgdmFyaWFibGUuDQoNCg0KYGBge3J9DQoNCmxpYnJhcnkoY29ycnBsb3QpDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCiNWaXN1YWxpemUgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGJldHdlZW4gIkFnZSIgYW5kIGFsbCBvdGhlciB2YXJpYWJsZXMNCg0KY29tcGFyZV9hZ2UgPC0gZGF0YS5mcmFtZShjKHNjWzNdLCBzY1s2OjExXSwgc2NbMTY6MTddLCBzY1sxOToyMF0pKQ0KY29ycl9hZ2UgPC0gY29yKGNvbXBhcmVfYWdlKQ0KY29ycnBsb3QubWl4ZWQoY29ycl9hZ2UsIGxvd2VyID0gJ251bWJlcicsIHVwcGVyID0gJ2VsbGlwc2UnLCBjb2wgPSB2aXJpZGlzKDI1NiksIHRpdGxlID0gJ0NvcnJlbGF0aW9ucyB3aXRoIFBsYXllciBBZ2UnLCBtYXI9YygwLDAsMSwwKSkNCg0KI1Zpc3VhbGl6ZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgYmV0d2VlbiAiSG91cnNQZXJXZWVrIiBhbmQgYWxsIG90aGVyIHZhcmlhYmxlcw0KDQpjb21wYXJlX3dlZWsgPC0gZGF0YS5mcmFtZShjKHNjWzRdLCBzY1s2OjExXSwgc2NbMTY6MTddLCBzY1sxOToyMF0pKQ0KY29ycl93ZWVrIDwtIGNvcihjb21wYXJlX3dlZWspDQpjb3JycGxvdC5taXhlZChjb3JyX3dlZWssIGxvd2VyID0gJ251bWJlcicsIHVwcGVyID0gJ2VsbGlwc2UnLCBjb2wgPSB2aXJpZGlzKDI1NiksIHRpdGxlID0gJ0NvcnJlbGF0aW9ucyB3aXRoIEhvdXJzIFBsYXllZCBwZXIgV2VlaycsIG1hcj1jKDAsMCwxLDApKQ0KDQojVmlzdWFsaXplIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBiZXR3ZWVuICJUb3RhbEhvdXJzIiBhbmQgYWxsIG90aGVyIHZhcmlhYmxlcw0KDQpjb21wYXJlX3RvdGFsIDwtIGRhdGEuZnJhbWUoYyhzY1s1XSwgc2NbNjoxMV0sIHNjWzE2OjE3XSwgc2NbMTk6MjBdKSkNCmNvcnJfdG90YWwgPC0gY29yKGNvbXBhcmVfdG90YWwpDQpjb3JycGxvdC5taXhlZChjb3JyX3RvdGFsLCBsb3dlciA9ICdudW1iZXInLCB1cHBlciA9ICdlbGxpcHNlJywgY29sID0gdmlyaWRpcygyNTYpLCB0aXRsZSA9ICdDb3JyZWxhdGlvbnMgd2l0aCBUb3RhbCBIb3VycyBQbGF5ZWQnLCBtYXI9YygwLDAsMSwwKSkNCg0KYGBgDQoNCg0KPiBBdCBmaXJzdCBnbGFuY2UsIGl0IHNlZW1zIGxpa2UgdGhlcmUgYXJlIHNvbWUgY29ycmVsYXRpb25zIGJldHdlZW4gc29tZSBtZXRyaWNzIGFuZCB0aGUgdGhyZWUgdGltZSB2YXJpYWJsZXMuIE1vcmUgaW1wb3J0YW50bHksIGl0IGxvb2tzIGxpa2UgYWxsIGJ1dCBvbmUgb2YgdGhlIG1ldHJpY3MgYXJlIG5lZ2F0aXZlbHkgY29ycmVsYXRlZCB3aXRoIGFnZSBhbmQgcG9zaXRpdmVseSBjb3JyZWxhdGVkIHdpdGggcGxheWluZyBtb3JlIGhvdXJzIHBlciB3ZWVrIGFuZCBpbiB0b3RhbC4gSW50ZXJlc3RpbmdseSBlbm91Z2gsIGZvciBhbGwgdGhyZWUgdGltZSB2YXJpYWJsZXMsIHRoZSB0aHJlZSB2YXJpYWJsZXMgd2l0aCB0aGUgZ3JlYXRlc3QgbWFnbml0dWRlIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50cyBhcmUgKkFQTSosICpTZWxlY3RCeUhvdGtleXMqLCBhbmQgKkFzc2lnblRvSG90a2V5cyouIA0KDQoNCmBgYHtyfQ0KDQpnZ3Bsb3Qoc2MsIGFlcyhBZ2UsIEFQTSkpICsgZ2VvbV9wb2ludChjb2xvciA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuMjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnUGxheWVyIEFnZScsIHkgPSAnQWN0aW9ucyBQZXIgTWludXRlJykgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDQwMCwgMjUpKSkNCg0KZ2dwbG90KHNjLCBhZXMoQWdlLCBTZWxlY3RCeUhvdGtleXMqODguNSo2MCkpICsgZ2VvbV9wb2ludChjb2xvciA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuMjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnUGxheWVyIEFnZScsIHkgPSAnSG90a2V5IEdyb3VwcyBTZWxlY3RlZCBwZXIgTWludXRlJykgICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjIsIGFlcyhncm91cCA9IDEpKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDIwMCwgMjUpKSkNCg0KZ2dwbG90KHNjLCBhZXMoQWdlLCBBc3NpZ25Ub0hvdGtleXMqODguNSo2MCkpICsgZ2VvbV9wb2ludChjb2xvciA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuMjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnUGxheWVyIEFnZScsIHkgPSAnSG90a2V5IEFzc2lnbm1lbnRzIE1hZGUgcGVyIE1pbnV0ZScpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjIsIGFlcyhncm91cCA9IDEpKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDcsIDAuNSkpKQ0KDQpgYGANCg0KDQo+IEl0IHNob3VsZCBiZSBub3RlZCB0aGF0IHRoZXJlIGlzIGFuIG9idmlvdXMgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHdvIGhvdGtleSB2YXJpYWJsZXMgYW5kICpBUE0qLCB3aGljaCBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCBwcm9wZXIgaG90a2V5IHVzYWdlIGNvbnRyaWJ1dGVzIGdyZWF0bHkgdG8gaGF2aW5nIGEgaGlnaCBBUE0gYnkgdmlydHVlIG9mIGFsbG93aW5nIGZhc3RlciBhbmQgbW9yZSBlZmZpY2llbnQgYWN0aW9ucywgd2hpY2ggc2F2ZSB0aGUgcGxheWVyIHRpbWUgdGhhdCBjYW4gYmUgc3BlbnQgb24gcGVyZm9ybWluZyBtb3JlIGFjdGlvbnMuIFNpbWlsYXJseSwgVGhpcyBpcyB0aGUgY2FzZSBmb3IgYWxsIG9mIHRoZSBvdGhlciB2YXJpYWJsZXMgc2hvdyBpbiB0aGUgY29ycmVsYXRpb24gbWF0cml4IGFzIHdlbGwuIFRoZXJlZm9yZSwgaXQgaXMgbmF0dXJhbCB0aGF0IHRoZXNlIHZhcmlhYmxlcyB3b3VsZCBiZSBjb3JyZWxhdGVkIGluIHRoZSBzYW1lIGRpcmVjdGlvbi4gDQoNCg0KPiBPbmUgY2FuIHNlZSB0aGF0IHRoZSB2YXJpYW5jZSBmb3IgYW55IGdpdmVuIGFnZSBncm91cCBkZWNyZWFzZXMgd2l0aCBhZ2UgZHVlIHRvIHRoZSBmYWN0IHRoYXQgc2FtcGxlIHNpemUgZm9yIG9sZGVyIHBsYXllcnMgaXMgbXVjaCBzbWFsbGVyIHRoYW4gdGhvc2UgaW4gdGhlaXIgdGVlbnMgYW5kIGVhcmx5IHR3ZW50aWVzLiBUaGVyZWZvcmUsIGl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgc29tZSBldmlkZW5jZSB0aGF0IGFnZSBuZWdhdGl2ZWx5IGFmZmVjdHMgdGhlc2UgbWV0cmljcy4NCg0KPiBUaGUgb25seSB2YXJpYWJsZSB3aXRoIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCAqQWdlKiBpcyAqVW5pcXVlSG90S2V5cyouDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKEFnZSwgVW5pcXVlSG90a2V5cyo4OC41KjYwKSkgKyBnZW9tX3BvaW50KGNvbG9yID0gJ3B1cnBsZScsIGFscGhhID0gMC4yNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdQbGF5ZXIgQWdlJywgeSA9ICdVbmlxdWUgSG90a2V5cyBVc2VkIHBlciBNaW51dGUnKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNlID0gRkFMU0UsIGNvbG9yID0gJ2JsdWUnLCBzaXplID0gMS4yLCBhZXMoZ3JvdXAgPSAxKSkgKyBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgMiwgMC4xKSkpDQoNCmBgYA0KDQoNCj4gVGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIGxpbmUgbG9va3MgdG8gYmUgYWxtb3N0IDAgdW5sZXNzIG9uZSBpcyBsb29raW5nIGNsb3NlbHkuIFRoZXJlZm9yZSwgaXQgc2VlbXMgdGhhdCBhZ2UgZG9lcyBub3QgaGF2ZSBtdWNoIG9mIGFuIGVmZmVjdCBvbiBob3cgcGxheWVycyBjdXN0b21pemUgdGhlaXIgaG90a2V5IHNldHVwLiBUaGlzIGNvdWxkIGJlIGluIHBhcnQgZHVlIHRvIG91ciBwcmV2aW91cyBmaW5kaW5nIHRoYXQgKlVuaXF1ZUhvdGtleXMqIHNlZW1zIHRvIGJlIHRoZSBsZWFzdCBpbXBvcnRhbnQgbWV0cmljIGluIHRoZSBza2lsbCBoZWlyYXJjaHkuDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKEhvdXJzUGVyV2VlaywgQVBNKSkgKyBnZW9tX3BvaW50KGNvbG9yID0gJ3B1cnBsZScsIGFscGhhID0gMC4yNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdSZXBvcnRlZCBIb3VycyBQbGF5ZWQgUGVyIFdlZWsnLCB5ID0gJ0FjdGlvbnMgUGVyIE1pbnV0ZScpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjIsIGFlcyhncm91cCA9IDEpKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCA0MDAsIDI1KSkpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDEwMCwgYnkgPSA1KSkpDQoNCmdncGxvdChzYywgYWVzKEhvdXJzUGVyV2VlaywgU2VsZWN0QnlIb3RrZXlzKjg4LjUqNjApKSArIGdlb21fcG9pbnQoY29sb3IgPSAncHVycGxlJywgYWxwaGEgPSAwLjI1KSArIHRoZW1lKHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICd3aGl0ZScpLCBwYW5lbC5ncmlkLm1ham9yID0gIGVsZW1lbnRfbGluZShjb2xvciA9ICdncmF5JykpICsgbGFicyh4ID0gJ1JlcG9ydGVkIEhvdXJzIFBsYXllZCBQZXIgV2VlaycsIHkgPSAnSG90a2V5IEdyb3VwcyBTZWxlY3RlZCBwZXIgTWludXRlJykgICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJywgc2UgPSBGQUxTRSwgY29sb3IgPSAnYmx1ZScsIHNpemUgPSAxLjIsIGFlcyhncm91cCA9IDEpKSArIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLDIwMCwgMjUpKSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgMTAwLCBieSA9IDUpKSkNCg0KZ2dwbG90KHNjLCBhZXMoSG91cnNQZXJXZWVrLCBBc3NpZ25Ub0hvdGtleXMqODguNSo2MCkpICsgZ2VvbV9wb2ludChjb2xvciA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuMjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnUmVwb3J0ZWQgSG91cnMgUGxheWVkIFBlciBXZWVrJywgeSA9ICdIb3RrZXkgQXNzaWdubWVudHMgTWFkZSBwZXIgTWludXRlJykgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsNywgMC41KSkpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDEwMCwgNSkpKQ0KDQpgYGANCg0KDQo+IFRoZXNlIHRocmVlIHZhcmlhYmxlcyBleGhpYml0IGEgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCAqSG91cnNQZXJXZWVrKiwgd2l0aCBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgdGhhdCBoYXZlIHNsaWdodGx5IGhpZ2hlciBtYWduaXR1ZGVzIHdoZW4gY29tcGFyZWQgdG8gdGhvc2Ugb2YgKkFnZSouDQoNCg0KYGBge3J9DQoNCmdncGxvdChzYywgYWVzKFRvdGFsSG91cnMsIEFQTSkpICsgZ2VvbV9wb2ludChjb2xvciA9ICdwdXJwbGUnLCBhbHBoYSA9IDAuMjUpICsgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gJ3doaXRlJyksIHBhbmVsLmdyaWQubWFqb3IgPSAgZWxlbWVudF9saW5lKGNvbG9yID0gJ2dyYXknKSkgKyBsYWJzKHggPSAnUmVwb3J0ZWQgVG90YWwgTnVtYmVyIG9mIEhvdXJzIFBsYXllZCcsIHkgPSAnQWN0aW9ucyBQZXIgTWludXRlJykgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsIDQwMCwgMjUpKSkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgNDAwMCwgMjUwKSkpDQoNCmdncGxvdChzYywgYWVzKFRvdGFsSG91cnMsIFNlbGVjdEJ5SG90a2V5cyo4OC41KjYwKSkgKyBnZW9tX3BvaW50KGNvbG9yID0gJ3B1cnBsZScsIGFscGhhID0gMC4yNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdSZXBvcnRlZCBUb3RhbCBOdW1iZXIgb2YgSG91cnMgUGxheWVkJywgeSA9ICdIb3RrZXkgR3JvdXBzIFNlbGVjdGVkIHBlciBNaW51dGUnKSAgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsMjAwLCAyNSkpKSArICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYyhzZXEoMCwgNDAwMCwgMjUwKSkpDQoNCmdncGxvdChzYywgYWVzKFRvdGFsSG91cnMsIEFzc2lnblRvSG90a2V5cyo4OC41KjYwKSkgKyBnZW9tX3BvaW50KGNvbG9yID0gJ3B1cnBsZScsIGFscGhhID0gMC4yNSkgKyB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAnd2hpdGUnKSwgcGFuZWwuZ3JpZC5tYWpvciA9ICBlbGVtZW50X2xpbmUoY29sb3IgPSAnZ3JheScpKSArIGxhYnMoeCA9ICdSZXBvcnRlZCBUb3RhbCBOdW1iZXIgb2YgSG91cnMgUGxheWVkJywgeSA9ICdIb3RrZXkgQXNzaWdubWVudHMgTWFkZSBwZXIgTWludXRlJykgKyBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nLCBzZSA9IEZBTFNFLCBjb2xvciA9ICdibHVlJywgc2l6ZSA9IDEuMiwgYWVzKGdyb3VwID0gMSkpICsgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IGMoc2VxKDAsNywgMC41KSkpICsgIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKHNlcSgwLCA0MDAwLCAyNTApKSkNCg0KYGBgDQoNCg0KPiBUaGlzIHNldCBvZiBzY2F0dGVycGxvdHMgc2hvdyBhbiBldmVuIGdyZWF0ZXIgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdGhyZWUgdmFyaWFibGVzIGFuZCAqVG90YWxIb3VycyouIEFsdGhvdWdoIHRoZXNlIGZpbmRpbmdzIG1pZ2h0IG5vdCBiZSBjb21wbGV0ZWx5IGFjY3VyYXRlIGR1ZSB0byB0aGUgdmFsdWVzIGJlaW5nIHNlbGYtcmVwb3J0ZWQsIEkgZmVlbCB0aGF0IHRoZSBjb3JyZWxhdGlvbiBpcyBzdHJvbmcgZW5vdWdoIHRvIHdhcnJhbnQgZnV0dXJlIHJlZWFyY2ggaW50byB0aGUgcmVsYXRpb25zaGlwcyBhbW9uZyB0aGVzZSB2YXJpYWJsZXMuDQoNCg0KIyM2LiBDb25jbHVzaW9uDQoNCg0KPiBBcyBtZW50aW9uZWQgZHVyaW5nIHRoZSBpbnRyb2R1Y3Rpb24gYW5kIG1haW4gYW5hbHlzaXMsIGEgbWFqb3IgbGltaXRhdGlvbiBvZiB0aGlzIGRhdGFzZXQgaXMgdGhlIGZhY3QgdGhhdCB0aGUgdGltZSB2YXJpYWJsZXMgYXJlIHNlbGYtcmVwb3J0ZWQgYnkgcGFydGljaXBhbnRzLiBBbHNvLCBzaW5jZSBlYWNoIGRhdGEgcG9pbnQgb25seSByZXByZXNlbnRzIG9uZSBwbGF5ZXIncyBza2lsbCBsZXZlbCBhdCBhIHNpbmdsZSBwb2ludCBpbiB0aW1lIChvbmUgZ2FtZSksIHRoZXJlIGlzbid0IGEgd2F5IHRvIG9ic2VydmUgdGhhdCBwbGF5ZXIncyBza2lsbCBvdmVyIHRpbWUuIEJ5IGRlZmF1bHQsIFNDMiBzYXZlcyBhIGRhdGVkIHJlcGxheSBmb3IgZXZlcnkgc2luZ2xlIGdhbWUgcGxheWVkIHRvIGEgbG9jYWwgZm9sZGVyLiBFdmVuIHdoZW4gdGhlIGdhbWUgaXMgdW5pbnN0YWxsZWQsIHRoaXMgZm9sZGVyIGlzIG5vdCBhdXRvbWF0aWNhbGx5IHJlbW92ZWQgZnJvbSB0aGUgY29tcHV0ZXIuIFRoZXJlZm9yZSwgdGhlc2UgcHJvYmxlbXMgY291bGQgYmUgc29sdmVkIGJ5IGNvbGxlY3RpbmcgYSBzZWNvbmQsIG1vcmUgY29tcHJlaGVuc2l2ZSBkYXRhc2V0LCBvbmUgd2hlcmUgZWFjaCBwYXJ0aWNpcGFudCBzdWJtaXRzIHRoZSBlbnRpcmUgY29udGVudHMgb2YgdGhlaXIgcmVwbGF5IGZvbGRlciAob3IgYXMgbWFueSBvZiB0aGUgcmVwbGF5cyBhcyBwb3NzaWJsZSkuIFdpdGggc3VjaCBhIGRhdGFzZXQsIHBsYXllciBwZXJmb3JtYW5jZSBjYW4gYmUgdHJhY2tlZCBhY3Jvc3MgYSBsb25nZXIgcGVyaW9kIG9mIHRpbWUuIEluIGFkZGl0aW9uLCAqVG90YWxIb3VycyogY2FuIGJlIGRpcmVjdGx5IGNhbGN1bGF0ZWQgYnkgc3VtbWluZyB0aGUgbGVuZ3RocyBvZiBldmVyeSBnYW1lIGluIHRoZSByZXBsYXkgZm9sZGVyLiAqSG91cnNQZXJXZWVrKiBjYW4gc3Vic2VxdWVudGx5IGJlIGNhbGN1bGF0ZWQgYnkgYmlubmluZyB0aGUgZ2FtZXMgYnkgY2FsZW5kYXIgd2VlayBiYXNlZCBvbiB0aGUgZGF0ZXMgaW4gdGhlIGZpbGUgbmFtZXMgYW5kIHRha2luZyBhbiBhdmVyYWdlIGFjcm9zcyB0aGUgdG90YWwgbnVtYmVyIG9mIHdlZWtzIHNwYW5uZWQgYnkgZWFjaCBwbGF5ZXIncyByZXBsYXkgZmlsZSBjb2xsZWN0aW9uLg0KDQoNCj4gVGhlIG1haW4gbGVzc29uIHRoYXQgSSBsZWFybmVkIGZyb20gYW5hbHl6aW5nIHRoaXMgZGF0YXNldCBpcyB0byBiZSBza2VwdGljYWwgb2YgdGhlIHZhcmlhYmxlIG5hbWVzIGFuZCBkZXNjcmlwdGlvbnMgcHJvdmlkZWQgYnkgd2hvZXZlciBjb2xsZWN0ZWQgdGhlIGRhdGEuIFRoZXNlIGRlc2NyaXB0aW9ucyBjb3VsZCBiZSB2YWd1ZSBvciBldmVuIGluY29ycmVjdC4gRG9tYWluIGtub3dsZWRnZSBpcyB2YWx1YWJsZSBpbiBkZWNpZGluZyB3aGV0aGVyIG9uZSdzIGludGVycHJldGF0aW9uIG9mIGEgdmFndWVseSBkZXNjcmliZWQgdmFyaWFibGUgaXMgaW5jb3JyZWN0LiBXaXRob3V0IGRvbWFpbiBrbm93bGVkZ2UsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gdHJ5IHRyYW5zZm9ybWluZyB0aGUgdmFyaWFibGVzIHRvIGEgbW9yZSBmYW1pbGlhciBmb3JtIChpbiB0aGlzIGNhc2UsIGNvbnZlcnRpbmcgcmF0ZSB2YXJpYWJsZXMgZnJvbSB0aW1lc3RhbXBzIHRvIG1pbnV0ZXMpLiBUaGlzIHRyYW5zZm9ybWF0aW9uLCBjb21iaW5lZCB3aXRoIHNvbWUgcmVzZWFyY2ggb24gdGhlIGNvbnRleHQgYW5kIG9yaWdpbiBvZiB0aGUgZGF0YXNldCwgY2FuIGJlIGhlbHBmdWwgaW4gZGVjaWRpbmcgaWYgdGhlIHJlc3VsdGluZyB2YWx1ZXMgYXJlIHNlbnNpYmxlIGdpdmVuIHRoZSBwcm9wb3NlZCBkZWZpbml0aW9uIG9mIHRoZSB2YXJpYWJsZS4NCg0KDQo=